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

     1  package ebpftracer
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"strings"
     9  	"sync"
    10  	"sync/atomic"
    11  
    12  	"github.com/castai/kvisor/pkg/logging"
    13  	"github.com/castai/kvisor/pkg/proc"
    14  	"github.com/cilium/ebpf"
    15  	"github.com/cilium/ebpf/btf"
    16  	"github.com/cilium/ebpf/rlimit"
    17  	"golang.org/x/sys/unix"
    18  )
    19  
    20  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -type global_config_t -no-global-types -cc clang-14 -strip=llvm-strip -target arm64 tracer ./c/tracee.bpf.c -- -I./c/headers -Wno-address-of-packed-member -O2
    21  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -type global_config_t -no-global-types -cc clang-14 -strip=llvm-strip -target amd64 tracer ./c/tracee.bpf.c -- -I./c/headers -Wno-address-of-packed-member -O2
    22  
    23  type moduleConfig struct {
    24  	BTFObjPath string
    25  }
    26  
    27  func newModule(log *logging.Logger, cfg moduleConfig) *module {
    28  	return &module{
    29  		log:            log,
    30  		cfg:            cfg,
    31  		loaded:         &atomic.Bool{},
    32  		attachedProbes: map[handle]struct{}{},
    33  	}
    34  }
    35  
    36  // module is responsible for loading ebpf objects (programs and maps).
    37  type module struct {
    38  	log     *logging.Logger
    39  	objects *tracerObjects
    40  	cfg     moduleConfig
    41  
    42  	loaded *atomic.Bool
    43  
    44  	probes         map[handle]probe
    45  	attachedProbes map[handle]struct{}
    46  	probesMu       sync.Mutex
    47  }
    48  
    49  func (m *module) load(targetPIDNSID proc.NamespaceID, flowSampleSubmitIntervalSeconds uint64) error {
    50  	if err := unix.Setrlimit(unix.RLIMIT_NOFILE, &unix.Rlimit{
    51  		Cur: 4096,
    52  		Max: 4096,
    53  	}); err != nil {
    54  		return fmt.Errorf("setting temporary rlimit: %w", err)
    55  	}
    56  	if err := rlimit.RemoveMemlock(); err != nil {
    57  		return err
    58  	}
    59  
    60  	objs := tracerObjects{}
    61  
    62  	spec, err := loadTracer()
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	var kernelTypes *btf.Spec
    68  	if m.cfg.BTFObjPath != "" {
    69  		kernelTypes, err = btf.LoadSpec(m.cfg.BTFObjPath)
    70  		if err != nil {
    71  			return fmt.Errorf("loading custom btf: %w", err)
    72  		}
    73  	}
    74  
    75  	if err := spec.RewriteConstants(map[string]interface{}{
    76  		"global_config": tracerGlobalConfigT{
    77  			PidNsId:                         targetPIDNSID,
    78  			FlowSampleSubmitIntervalSeconds: flowSampleSubmitIntervalSeconds,
    79  		},
    80  	}); err != nil {
    81  		return err
    82  	}
    83  
    84  	if err := spec.LoadAndAssign(&objs, &ebpf.CollectionOptions{
    85  		Maps: ebpf.MapOptions{},
    86  		Programs: ebpf.ProgramOptions{
    87  			LogLevel:    0,
    88  			LogSize:     200_000_000,
    89  			LogDisabled: false,
    90  			KernelTypes: kernelTypes,
    91  		},
    92  		MapReplacements: nil,
    93  	}); err != nil {
    94  		return err
    95  	}
    96  
    97  	m.objects = &objs
    98  
    99  	// TODO(Kvisord): Mount cgroupv2 if not mounted.
   100  	cgroupPath, err := detectCgroupPath()
   101  	if err != nil {
   102  		cgroupPath = "/cgroupv2"
   103  		m.log.Debugf("mounting cgroupv2 to path %s", cgroupPath)
   104  		if err := mountCgroup2(cgroupPath); err != nil {
   105  			return fmt.Errorf("mounting cgroupv2: %w", err)
   106  		}
   107  	}
   108  	m.probes = newProbes(m.objects, cgroupPath)
   109  
   110  	m.loaded.Store(true)
   111  
   112  	return nil
   113  }
   114  
   115  func (m *module) close() error {
   116  	if !m.loaded.Load() {
   117  		return nil
   118  	}
   119  
   120  	m.probesMu.Lock()
   121  	defer m.probesMu.Unlock()
   122  
   123  	// Close bpf probes.
   124  	for handle := range m.attachedProbes {
   125  		probe := m.probes[handle]
   126  		if err := probe.detach(); err != nil {
   127  			return fmt.Errorf("detach probe: %s: %w", probe.String(), err)
   128  		}
   129  	}
   130  
   131  	// Close programs.
   132  	return m.objects.Close()
   133  }
   134  
   135  func (m *module) attachProbe(handle handle) error {
   136  	m.probesMu.Lock()
   137  	defer m.probesMu.Unlock()
   138  
   139  	if _, found := m.attachedProbes[handle]; found {
   140  		return nil
   141  	}
   142  
   143  	probe, found := m.probes[handle]
   144  	if !found {
   145  		return fmt.Errorf("probe %d not registered", handle)
   146  	}
   147  	m.log.Debugf("attaching probe: %s", probe.String())
   148  	if err := probe.attach(); err != nil {
   149  		return err
   150  	}
   151  	m.attachedProbes[handle] = struct{}{}
   152  	return nil
   153  }
   154  
   155  func detectCgroupPath() (string, error) {
   156  	f, err := os.Open("/proc/mounts")
   157  	if err != nil {
   158  		return "", err
   159  	}
   160  	defer f.Close()
   161  
   162  	scanner := bufio.NewScanner(f)
   163  	for scanner.Scan() {
   164  		fields := strings.Split(scanner.Text(), " ")
   165  		if len(fields) >= 3 && fields[2] == "cgroup2" {
   166  			return fields[1], nil
   167  		}
   168  	}
   169  
   170  	return "", errors.New("cgroupv2 not found, need to mount")
   171  }