tetragon

Форк
0
/
load.go 
383 строки · 11.1 Кб
1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright Authors of Tetragon
3

4
package sensors
5

6
import (
7
	"fmt"
8
	"os"
9
	"path"
10
	"path/filepath"
11
	"strings"
12

13
	"github.com/cilium/ebpf"
14
	cachedbtf "github.com/cilium/tetragon/pkg/btf"
15
	"github.com/cilium/tetragon/pkg/kernels"
16
	"github.com/cilium/tetragon/pkg/logger"
17
	"github.com/cilium/tetragon/pkg/option"
18
	"github.com/cilium/tetragon/pkg/sensors/program"
19

20
	"github.com/sirupsen/logrus"
21
)
22

23
const (
24
	BPF_PROG_TYPE_UNSPEC                  = 0
25
	BPF_PROG_TYPE_SOCKET_FILTER           = 1
26
	BPF_PROG_TYPE_KPROBE                  = 2
27
	BPF_PROG_TYPE_SCHED_CLS               = 3
28
	BPF_PROG_TYPE_SCHED_ACT               = 4
29
	BPF_PROG_TYPE_TRACEPOINT              = 5
30
	BPF_PROG_TYPE_XDP                     = 6
31
	BPF_PROG_TYPE_PERF_EVENT              = 7
32
	BPF_PROG_TYPE_CGROUP_SKB              = 8
33
	BPF_PROG_TYPE_CGROUP_SOCK             = 9
34
	BPF_PROG_TYPE_LWT_IN                  = 10
35
	BPF_PROG_TYPE_LWT_OUT                 = 11
36
	BPF_PROG_TYPE_LWT_XMIT                = 12
37
	BPF_PROG_TYPE_SOCK_OPS                = 13
38
	BPF_PROG_TYPE_SK_SKB                  = 14
39
	BPF_PROG_TYPE_CGROUP_DEVICE           = 15
40
	BPF_PROG_TYPE_SK_MSG                  = 16
41
	BPF_PROG_TYPE_RAW_TRACEPOINT          = 17
42
	BPF_PROG_TYPE_CGROUP_SOCK_ADDR        = 18
43
	BPF_PROG_TYPE_LWT_SEG6LOCAL           = 19
44
	BPF_PROG_TYPE_LIRC_MODE2              = 20
45
	BPF_PROG_TYPE_SK_REUSEPORT            = 21
46
	BPF_PROG_TYPE_FLOW_DISSECTOR          = 22
47
	BPF_PROG_TYPE_CGROUP_SYSCTL           = 23
48
	BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE = 24
49
	BPF_PROG_TYPE_CGROUP_SOCKOPT          = 25
50
	BPF_PROG_TYPE_TRACING                 = 26
51
	BPF_PROG_TYPE_STRUCT_OPS              = 27
52
	BPF_PROG_TYPE_EXT                     = 28
53
	BPF_PROG_TYPE_LSM                     = 29
54
)
55

56
// LoadConfig loads the default sensor, including any from the configuration file.
57
func LoadConfig(bpfDir string, sens []*Sensor) error {
58
	load := mergeSensors(sens)
59
	if err := load.Load(bpfDir); err != nil {
60
		return fmt.Errorf("tetragon, aborting could not load BPF programs: %w", err)
61
	}
62
	return nil
63
}
64

65
// Load loads the sensor, by loading all the BPF programs and maps.
66
func (s *Sensor) Load(bpfDir string) error {
67
	if s == nil {
68
		return nil
69
	}
70

71
	if s.Destroyed {
72
		return fmt.Errorf("sensor %s has been previously destroyed, please recreate it before loading", s.Name)
73
	}
74

75
	// Add the loaded programs and maps to All* so they can be unloaded on shutdown.
76
	AllPrograms = append(AllPrograms, s.Progs...)
77
	AllMaps = append(AllMaps, s.Maps...)
78

79
	logger.GetLogger().WithField("metadata", cachedbtf.GetCachedBTFFile()).Info("BTF file: using metadata file")
80
	if _, err := observerMinReqs(); err != nil {
81
		return fmt.Errorf("tetragon, aborting minimum requirements not met: %w", err)
82
	}
83

84
	os.Mkdir(bpfDir, os.ModeDir)
85

86
	l := logger.GetLogger()
87

88
	l.WithField("name", s.Name).Info("Loading sensor")
89
	if s.Loaded {
90
		return fmt.Errorf("loading sensor %s failed: sensor already loaded", s.Name)
91
	}
92

93
	_, verStr, _ := kernels.GetKernelVersion(option.Config.KernelVersion, option.Config.ProcFS)
94
	l.Infof("Loading kernel version %s", verStr)
95

96
	if err := s.FindPrograms(); err != nil {
97
		return fmt.Errorf("tetragon, aborting could not find BPF programs: %w", err)
98
	}
99

100
	if err := s.loadMaps(bpfDir); err != nil {
101
		return fmt.Errorf("tetragon, aborting could not load sensor BPF maps: %w", err)
102
	}
103

104
	for _, p := range s.Progs {
105
		if p.LoadState.IsLoaded() {
106
			l.WithField("prog", p.Name).Info("BPF prog is already loaded, incrementing reference count")
107
			p.LoadState.RefInc()
108
			continue
109
		}
110

111
		if err := observerLoadInstance(bpfDir, p); err != nil {
112
			return err
113
		}
114
		p.LoadState.RefInc()
115
		l.WithField("prog", p.Name).WithField("label", p.Label).Debugf("BPF prog was loaded")
116
	}
117
	l.WithField("sensor", s.Name).Infof("Loaded BPF maps and events for sensor successfully")
118
	s.Loaded = true
119
	return nil
120
}
121

122
func (s *Sensor) Unload() error {
123
	logger.GetLogger().Infof("Unloading sensor %s", s.Name)
124
	if !s.Loaded {
125
		return fmt.Errorf("unload of sensor %s failed: sensor not loaded", s.Name)
126
	}
127

128
	if s.PreUnloadHook != nil {
129
		if err := s.PreUnloadHook(); err != nil {
130
			logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Pre unload hook failed")
131
		}
132
	}
133

134
	for _, p := range s.Progs {
135
		unloadProgram(p)
136
	}
137

138
	for _, m := range s.Maps {
139
		if err := m.Unload(); err != nil {
140
			logger.GetLogger().WithError(err).WithField("map", s.Name).Warn("Failed to unload map")
141
		}
142
	}
143

144
	s.Loaded = false
145

146
	if s.PostUnloadHook != nil {
147
		if err := s.PostUnloadHook(); err != nil {
148
			logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Post unload hook failed")
149
		}
150
	}
151

152
	return nil
153
}
154

155
// Destroy will unload the hook and call DestroyHook, this hook is usually used
156
// to clean up resources that were created during creation of the sensor.
157
func (s *Sensor) Destroy() {
158
	err := s.Unload()
159
	if err != nil {
160
		// do not return on error but just log since Unload can only error on
161
		// sensor being already not loaded
162
		logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Unload failed during destroy")
163
	}
164

165
	if s.DestroyHook != nil {
166
		err = s.DestroyHook()
167
		if err != nil {
168
			logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Destroy hook failed")
169
		}
170
	}
171
	s.Destroyed = true
172
}
173

174
func (s *Sensor) findProgram(p *program.Program) error {
175
	logger.GetLogger().WithField("file", p.Name).Debug("Checking for bpf file")
176
	if _, err := os.Stat(p.Name); err == nil {
177
		logger.GetLogger().WithField("file", p.Name).Debug("Found bpf file")
178
		return nil
179
	}
180
	logger.GetLogger().WithField("file", p.Name).Debug("Candidate bpf file does not exist")
181
	last := strings.Split(p.Name, "/")
182
	filename := last[len(last)-1]
183

184
	path := path.Join(option.Config.HubbleLib, filename)
185
	if _, err := os.Stat(path); err == nil {
186
		p.Name = path
187
		logger.GetLogger().WithField("file", path).Debug("Found bpf file")
188
		return nil
189
	}
190
	logger.GetLogger().WithField("file", path).Debug("Candidate bpf file does not exist")
191

192
	return fmt.Errorf("sensor program %q can not be found", p.Name)
193
}
194

195
// FindPrograms finds all the BPF programs in the sensor on the filesytem.
196
func (s *Sensor) FindPrograms() error {
197
	for _, p := range s.Progs {
198
		if err := s.findProgram(p); err != nil {
199
			return err
200
		}
201
	}
202
	for _, m := range s.Maps {
203
		if err := s.findProgram(m.Prog); err != nil {
204
			return err
205
		}
206
	}
207
	return nil
208
}
209

210
// loadMaps loads all the BPF maps in the sensor.
211
func (s *Sensor) loadMaps(bpfDir string) error {
212
	l := logger.GetLogger()
213
	for _, m := range s.Maps {
214
		if m.PinState.IsLoaded() {
215
			l.WithFields(logrus.Fields{
216
				"sensor": s.Name,
217
				"map":    m.Name,
218
			}).Info("map is already loaded, incrementing reference count")
219
			m.PinState.RefInc()
220
			continue
221
		}
222

223
		pinPath := filepath.Join(bpfDir, m.PinName)
224

225
		spec, err := ebpf.LoadCollectionSpec(m.Prog.Name)
226
		if err != nil {
227
			return fmt.Errorf("failed to open collection '%s': %w", m.Prog.Name, err)
228
		}
229
		mapSpec, ok := spec.Maps[m.Name]
230
		if !ok {
231
			return fmt.Errorf("map '%s' not found from '%s'", m.Name, m.Prog.Name)
232
		}
233

234
		if max, ok := m.GetMaxEntries(); ok {
235
			mapSpec.MaxEntries = max
236
		}
237

238
		if innerMax, ok := m.GetMaxInnerEntries(); ok {
239
			if innerMs := mapSpec.InnerMap; innerMs != nil {
240
				mapSpec.InnerMap.MaxEntries = innerMax
241
			}
242
		}
243

244
		if err := m.LoadOrCreatePinnedMap(pinPath, mapSpec); err != nil {
245
			return fmt.Errorf("failed to load map '%s' for sensor '%s': %w", m.Name, s.Name, err)
246
		}
247

248
		l.WithFields(logrus.Fields{
249
			"sensor": s.Name,
250
			"map":    m.Name,
251
			"path":   pinPath,
252
		}).Info("tetragon, map loaded.")
253
	}
254

255
	return nil
256
}
257

258
func mergeSensors(sensors []*Sensor) *Sensor {
259
	var progs []*program.Program
260
	var maps []*program.Map
261

262
	for _, s := range sensors {
263
		progs = append(progs, s.Progs...)
264
		maps = append(maps, s.Maps...)
265
	}
266
	return &Sensor{
267
		Name:  "__main__",
268
		Progs: progs,
269
		Maps:  maps,
270
	}
271
}
272

273
func observerLoadInstance(bpfDir string, load *program.Program) error {
274
	version, _, err := kernels.GetKernelVersion(option.Config.KernelVersion, option.Config.ProcFS)
275
	if err != nil {
276
		return err
277
	}
278

279
	l := logger.GetLogger()
280
	l.WithFields(logrus.Fields{
281
		"prog":         load.Name,
282
		"kern_version": version,
283
	}).Debug("observerLoadInstance", load.Name, version)
284
	if load.Type == "tracepoint" {
285
		err = loadInstance(bpfDir, load, version, option.Config.Verbosity)
286
		if err != nil {
287
			l.WithField(
288
				"tracepoint", load.Name,
289
			).Info("Failed to load, trying to remove and retrying")
290
			load.Unload()
291
			err = loadInstance(bpfDir, load, version, option.Config.Verbosity)
292
		}
293
		if err != nil {
294
			return fmt.Errorf("failed prog %s kern_version %d LoadTracingProgram: %w",
295
				load.Name, version, err)
296
		}
297
	} else if load.Type == "raw_tracepoint" || load.Type == "raw_tp" {
298
		err = loadInstance(bpfDir, load, version, option.Config.Verbosity)
299
		if err != nil {
300
			l.WithField(
301
				"raw_tracepoint", load.Name,
302
			).Info("Failed to load, trying to remove and retrying")
303
			load.Unload()
304
			err = loadInstance(bpfDir, load, version, option.Config.Verbosity)
305
		}
306
		if err != nil {
307
			return fmt.Errorf("failed prog %s kern_version %d LoadRawTracepointProgram: %w",
308
				load.Name, version, err)
309
		}
310
	} else {
311
		err = loadInstance(bpfDir, load, version, option.Config.Verbosity)
312
		if err != nil && load.ErrorFatal {
313
			return fmt.Errorf("failed prog %s kern_version %d loadInstance: %w",
314
				load.Name, version, err)
315
		}
316
	}
317
	return nil
318
}
319

320
func loadInstance(bpfDir string, load *program.Program, version, verbose int) error {
321
	// Check if the load.type is a standard program type. If so, use the standard loader.
322
	loadFn, ok := standardTypes[load.Type]
323
	if ok {
324
		logger.GetLogger().WithField("Program", load.Name).
325
			WithField("Type", load.Type).
326
			WithField("Attach", load.Attach).
327
			Info("Loading BPF program")
328
		return loadFn(bpfDir, load, verbose)
329
	}
330
	// Otherwise, check for a registered probe type. If one exists, use that.
331
	probe, ok := registeredProbeLoad[load.Type]
332
	if ok {
333
		logger.GetLogger().WithField("Program", load.Name).
334
			WithField("Type", load.Type).
335
			WithField("Attach", load.Attach).
336
			Info("Loading registered BPF probe")
337
		// Registered probes need extra setup
338
		version = kernels.FixKernelVersion(version)
339
		return probe.LoadProbe(LoadProbeArgs{
340
			BPFDir:  bpfDir,
341
			Load:    load,
342
			Version: version,
343
			Verbose: verbose,
344
		})
345
	}
346

347
	return fmt.Errorf("program %s has unregistered type '%s'", load.Label, load.Type)
348
}
349

350
func observerMinReqs() (bool, error) {
351
	_, _, err := kernels.GetKernelVersion(option.Config.KernelVersion, option.Config.ProcFS)
352
	if err != nil {
353
		return false, fmt.Errorf("kernel version lookup failed, required for kprobe")
354
	}
355
	return true, nil
356
}
357

358
func unloadProgram(prog *program.Program) {
359
	log := logger.GetLogger().WithField("label", prog.Label).WithField("pin", prog.PinPath)
360

361
	if !prog.LoadState.IsLoaded() {
362
		log.Debugf("Refusing to remove %s, program not loaded", prog.Label)
363
		return
364
	}
365
	if count := prog.LoadState.RefDec(); count > 0 {
366
		log.Debugf("Program reference count %d, not unloading yet", count)
367
		return
368
	}
369

370
	if err := prog.Unload(); err != nil {
371
		logger.GetLogger().WithField("name", prog.Name).WithError(err).Warn("Failed to unload program")
372
	}
373

374
	log.Info("BPF prog was unloaded")
375
}
376

377
func UnloadSensors(sens []SensorIface) {
378
	for i := range sens {
379
		if err := sens[i].Unload(); err != nil {
380
			logger.GetLogger().Warnf("Failed to unload sensor: %s", err)
381
		}
382
	}
383
}
384

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.