github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/pkg/ebpftracer/tracer_test.go (about)

     1  package ebpftracer_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"log/slog"
     8  	"os"
     9  	"testing"
    10  	"time"
    11  
    12  	castaipb "github.com/castai/kvisor/api/v1/runtime"
    13  	"github.com/castai/kvisor/pkg/cgroup"
    14  	"github.com/castai/kvisor/pkg/containers"
    15  	"github.com/castai/kvisor/pkg/ebpftracer"
    16  	"github.com/castai/kvisor/pkg/ebpftracer/events"
    17  	"github.com/castai/kvisor/pkg/ebpftracer/signature"
    18  	"github.com/castai/kvisor/pkg/ebpftracer/types"
    19  	"github.com/castai/kvisor/pkg/logging"
    20  	"github.com/castai/kvisor/pkg/proc"
    21  	"github.com/davecgh/go-spew/spew"
    22  )
    23  
    24  func TestTracer(t *testing.T) {
    25  	if testing.Short() {
    26  		t.Skip()
    27  	}
    28  
    29  	ctx := context.Background()
    30  
    31  	log := logging.New(&logging.Config{
    32  		Level: slog.LevelDebug,
    33  	})
    34  
    35  	procHandle := proc.New()
    36  	pidNS, err := procHandle.GetCurrentPIDNSID()
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  
    41  	tr := ebpftracer.New(log, ebpftracer.Config{
    42  		//BTFPath:              fmt.Sprintf("./testdata/5.10.0-0.deb10.24-cloud-%s.btf", runtime.GOARCH),
    43  		EventsPerCPUBuffer:    os.Getpagesize() * 64,
    44  		EventsOutputChanSize:  10000,
    45  		DefaultCgroupsVersion: "V2",
    46  		DebugEnabled:          true,
    47  		AllowAnyEvent:         true,
    48  		ContainerClient: &ebpftracer.MockContainerClient{
    49  			ContainerGetter: func(ctx context.Context, cgroupID uint64) (*containers.Container, error) {
    50  				dummyContainerID := fmt.Sprint(cgroupID)
    51  				return &containers.Container{
    52  					ID:           dummyContainerID,
    53  					Name:         "dummy-container",
    54  					CgroupID:     cgroupID,
    55  					PodNamespace: "default",
    56  					PodUID:       dummyContainerID,
    57  					PodName:      "dummy-container-" + dummyContainerID,
    58  					Cgroup: &cgroup.Cgroup{
    59  						Id:               cgroupID,
    60  						Version:          cgroup.V2,
    61  						ContainerRuntime: cgroup.ContainerdRuntime,
    62  						ContainerID:      dummyContainerID,
    63  						Path:             "",
    64  					},
    65  					PIDs: []uint32{},
    66  				}, nil
    67  			},
    68  		},
    69  		CgroupClient:                       &ebpftracer.MockCgroupClient{},
    70  		MountNamespacePIDStore:             getInitializedMountNamespacePIDStore(procHandle),
    71  		HomePIDNS:                          pidNS,
    72  		NetflowSampleSubmitIntervalSeconds: 1,
    73  	})
    74  	defer tr.Close()
    75  
    76  	if err := tr.Load(); err != nil {
    77  		t.Fatalf("load: %v", err)
    78  	}
    79  
    80  	errc := make(chan error, 1)
    81  	go func() {
    82  		errc <- tr.Run(ctx)
    83  	}()
    84  
    85  	signatures, err := signature.DefaultSignatures(log, signature.DefaultSignatureConfig{
    86  		TTYDetectedSignatureEnabled:    true,
    87  		SOCKS5DetectedSignatureEnabled: true,
    88  	})
    89  	if err != nil {
    90  		t.Fatalf("error while configuring signatures: %v", err)
    91  	}
    92  
    93  	signatureEngine := signature.NewEngine(signatures, log, signature.SignatureEngineConfig{
    94  		InputChanSize:  100,
    95  		OutputChanSize: 100,
    96  	})
    97  	sigerr := make(chan error, 1)
    98  	go func() {
    99  		sigerr <- signatureEngine.Run(ctx)
   100  	}()
   101  
   102  	policy := &ebpftracer.Policy{
   103  		Events: []*ebpftracer.EventPolicy{
   104  			{ID: events.NetFlowBase},
   105  			//{ID: events.SecuritySocketConnect},
   106  			//{ID: events.SockSetState},
   107  			//{ID: events.NetPacketDNSBase},
   108  		},
   109  	}
   110  
   111  	if err := tr.ApplyPolicy(policy); err != nil {
   112  		t.Fatalf("apply policy: %v", err)
   113  	}
   114  
   115  	//go printSyscallStatsLoop(ctx, tr, log)
   116  
   117  	for {
   118  		select {
   119  		case <-ctx.Done():
   120  			return
   121  		case err := <-sigerr:
   122  			t.Fatal(err)
   123  		case s := <-signatureEngine.Events():
   124  			printSignatureEvent(s)
   125  		case e := <-tr.Events():
   126  			printEvent(tr, e)
   127  		case e := <-tr.NetflowEvents():
   128  			printEvent(tr, e)
   129  		case err := <-errc:
   130  			if err != nil {
   131  				t.Fatal(err)
   132  			}
   133  		}
   134  	}
   135  }
   136  
   137  func printEvent(tr *ebpftracer.Tracer, e *types.Event) {
   138  	eventName := tr.GetEventName(e.Context.EventID)
   139  	fmt.Printf(
   140  		"ts=%d cgroup=%d, pid=%d, proc=%s, event=%s, args=%+v",
   141  		e.Context.Ts,
   142  		e.Context.CgroupID,
   143  		e.Context.HostPid,
   144  		string(bytes.TrimRight(e.Context.Comm[:], "\x00")),
   145  		eventName,
   146  		e.Args,
   147  	)
   148  	if e.Context.EventID == events.NetFlowBase {
   149  		fmt.Printf(" ret=%d direction=%s, type=%s, initiator=%v", e.Context.Retval, e.Context.GetFlowDirection(), e.Context.GetNetflowType(), e.Context.IsSourceInitiator())
   150  	}
   151  	fmt.Print("\n")
   152  }
   153  
   154  func printSignatureEvent(e *castaipb.Event) {
   155  	fmt.Printf(
   156  		"cgroup=%d, pid=%d, proc=%s, event=%s,args=%+v",
   157  		e.CgroupId,
   158  		e.HostPid,
   159  		e.ProcessName,
   160  		e.EventType,
   161  		e.Data,
   162  	)
   163  	fmt.Print("\n")
   164  }
   165  
   166  func getInitializedMountNamespacePIDStore(procHandler *proc.Proc) *types.PIDsPerNamespace {
   167  	mountNamespacePIDStore, err := types.NewPIDsPerNamespaceCache(2048, 5)
   168  	if err != nil {
   169  		panic(err)
   170  	}
   171  
   172  	processes, err := procHandler.LoadMountNSOldestProcesses()
   173  	if err != nil {
   174  		panic(err)
   175  	}
   176  
   177  	for ns, pid := range processes {
   178  		mountNamespacePIDStore.ForceAddToBucket(ns, pid)
   179  	}
   180  
   181  	return mountNamespacePIDStore
   182  }
   183  
   184  func printSyscallStatsLoop(ctx context.Context, tr *ebpftracer.Tracer, log *logging.Logger) {
   185  	for {
   186  		select {
   187  		case <-ctx.Done():
   188  			return
   189  		case <-time.After(5 * time.Second):
   190  			stats, err := tr.ReadSyscallStats()
   191  			if err != nil {
   192  				log.Errorf("reading syscall stats: %v", err)
   193  				continue
   194  			}
   195  			spew.Dump(stats)
   196  		}
   197  	}
   198  }