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 }