inspektor-gadget

Форк
0
292 строки · 7.4 Кб
1
// Copyright 2019-2024 The Inspektor Gadget authors
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
//     http://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
//go:build !withoutebpf
16

17
package tracer
18

19
import (
20
	"errors"
21
	"fmt"
22
	"io/fs"
23
	"os"
24
	"runtime"
25
	"unsafe"
26

27
	"github.com/cilium/ebpf"
28
	"github.com/cilium/ebpf/link"
29
	"github.com/cilium/ebpf/perf"
30

31
	gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
32
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
33
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/open/types"
34
	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
35
)
36

37
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -no-global-types -target bpfel -cc clang -cflags ${CFLAGS} -type event -type prefix_key opensnoop ./bpf/opensnoop.bpf.c -- -I./bpf/
38

39
const (
40
	// Keep in sync with opensnoop.h.
41
	NAME_MAX = 255
42
	// Keep in sync with opensnoop.bpf.c.
43
	CHAR_BIT = 8
44
)
45

46
// needs to be kept in sync with opensnoopEvent from opensnoop_bpfel.go without the FullFname field
47
type opensnoopEventAbbrev struct {
48
	Timestamp uint64
49
	Pid       uint32
50
	Uid       uint32
51
	Gid       uint32
52
	_         [4]byte
53
	MntnsId   uint64
54
	Ret       int32
55
	Flags     int32
56
	Mode      uint16
57
	Comm      [16]uint8
58
	Fname     [255]uint8
59
}
60

61
type Config struct {
62
	MountnsMap *ebpf.Map
63
	FullPath   bool
64
	Prefixes   []string
65
}
66

67
type Tracer struct {
68
	config        *Config
69
	enricher      gadgets.DataEnricherByMntNs
70
	eventCallback func(*types.Event)
71

72
	objs            opensnoopObjects
73
	openEnterLink   link.Link
74
	openAtEnterLink link.Link
75
	openExitLink    link.Link
76
	openAtExitLink  link.Link
77
	reader          *perf.Reader
78
}
79

80
func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs,
81
	eventCallback func(*types.Event),
82
) (*Tracer, error) {
83
	t := &Tracer{
84
		config:        config,
85
		enricher:      enricher,
86
		eventCallback: eventCallback,
87
	}
88

89
	if err := t.install(); err != nil {
90
		t.close()
91
		return nil, err
92
	}
93

94
	go t.run()
95

96
	return t, nil
97
}
98

99
// Stop stops the tracer
100
// TODO: Remove after refactoring
101
func (t *Tracer) Stop() {
102
	t.close()
103
}
104

105
func (t *Tracer) close() {
106
	t.openEnterLink = gadgets.CloseLink(t.openEnterLink)
107
	t.openAtEnterLink = gadgets.CloseLink(t.openAtEnterLink)
108
	t.openExitLink = gadgets.CloseLink(t.openExitLink)
109
	t.openAtExitLink = gadgets.CloseLink(t.openAtExitLink)
110

111
	if t.reader != nil {
112
		t.reader.Close()
113
	}
114

115
	t.objs.Close()
116
}
117

118
func (t *Tracer) install() error {
119
	spec, err := loadOpensnoop()
120
	if err != nil {
121
		return fmt.Errorf("loading ebpf program: %w", err)
122
	}
123

124
	prefixesNumber := uint32(len(t.config.Prefixes))
125
	prefixesMax := spec.Maps["prefixes"].MaxEntries
126
	if prefixesNumber > prefixesMax {
127
		return fmt.Errorf("%d maximum prefixes supported, got %d", prefixesMax, prefixesNumber)
128
	}
129

130
	consts := make(map[string]interface{})
131
	consts["get_full_path"] = t.config.FullPath
132
	consts["prefixes_nr"] = prefixesNumber
133

134
	for _, prefix := range t.config.Prefixes {
135
		var pfx [NAME_MAX]uint8
136

137
		bytes := uint32(len(prefix))
138
		if bytes > NAME_MAX {
139
			bytes = NAME_MAX
140
		}
141
		copy(pfx[:], prefix)
142

143
		spec.Maps["prefixes"].Contents = append(spec.Maps["prefixes"].Contents, ebpf.MapKV{
144
			// We need to give the exact length of the prefix here.
145
			// Otherwise, the kernel will compare until NAME_MAX * CHAR_BIT and there
146
			// will never be a match (unless the filename is NAME_MAX long and equals
147
			// to the prefix).
148
			Key:   opensnoopPrefixKey{Prefixlen: bytes * CHAR_BIT, Filename: pfx},
149
			Value: uint8(0),
150
		})
151
	}
152

153
	if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil {
154
		return fmt.Errorf("loading ebpf spec: %w", err)
155
	}
156

157
	// arm64 does not define the open() syscall, only openat().
158
	if runtime.GOARCH != "arm64" {
159
		openEnter, err := link.Tracepoint("syscalls", "sys_enter_open", t.objs.IgOpenE, nil)
160
		if err != nil {
161
			return fmt.Errorf("attaching tracepoint: %w", err)
162
		}
163
		t.openEnterLink = openEnter
164
	}
165

166
	openAtEnter, err := link.Tracepoint("syscalls", "sys_enter_openat", t.objs.IgOpenatE, nil)
167
	if err != nil {
168
		return fmt.Errorf("attaching tracepoint: %w", err)
169
	}
170
	t.openAtEnterLink = openAtEnter
171

172
	if runtime.GOARCH != "arm64" {
173
		openExit, err := link.Tracepoint("syscalls", "sys_exit_open", t.objs.IgOpenX, nil)
174
		if err != nil {
175
			return fmt.Errorf("attaching tracepoint: %w", err)
176
		}
177
		t.openExitLink = openExit
178
	}
179

180
	openAtExit, err := link.Tracepoint("syscalls", "sys_exit_openat", t.objs.IgOpenatX, nil)
181
	if err != nil {
182
		return fmt.Errorf("attaching tracepoint: %w", err)
183
	}
184
	t.openAtExitLink = openAtExit
185

186
	reader, err := perf.NewReader(t.objs.opensnoopMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
187
	if err != nil {
188
		return fmt.Errorf("creating perf ring buffer: %w", err)
189
	}
190
	t.reader = reader
191

192
	return nil
193
}
194

195
func (t *Tracer) run() {
196
	for {
197
		record, err := t.reader.Read()
198
		if err != nil {
199
			if errors.Is(err, perf.ErrClosed) {
200
				// nothing to do, we're done
201
				return
202
			}
203

204
			msg := fmt.Sprintf("Error reading perf ring buffer: %s", err)
205
			t.eventCallback(types.Base(eventtypes.Err(msg)))
206
			return
207
		}
208

209
		if record.LostSamples > 0 {
210
			msg := fmt.Sprintf("lost %d samples", record.LostSamples)
211
			t.eventCallback(types.Base(eventtypes.Warn(msg)))
212
			continue
213
		}
214

215
		bpfEvent := (*opensnoopEventAbbrev)(unsafe.Pointer(&record.RawSample[0]))
216

217
		ret := int(bpfEvent.Ret)
218
		fd := 0
219
		errval := 0
220

221
		if ret >= 0 {
222
			fd = ret
223
		} else {
224
			errval = -ret
225
		}
226

227
		mode := fs.FileMode(bpfEvent.Mode)
228

229
		event := types.Event{
230
			Event: eventtypes.Event{
231
				Type:      eventtypes.NORMAL,
232
				Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
233
			},
234
			WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId},
235
			Pid:           bpfEvent.Pid,
236
			Uid:           bpfEvent.Uid,
237
			Gid:           bpfEvent.Gid,
238
			Comm:          gadgets.FromCString(bpfEvent.Comm[:]),
239
			Ret:           ret,
240
			Fd:            fd,
241
			Err:           errval,
242
			FlagsRaw:      bpfEvent.Flags,
243
			Flags:         DecodeFlags(bpfEvent.Flags),
244
			ModeRaw:       mode,
245
			Mode:          mode.String(),
246
			Path:          gadgets.FromCString(bpfEvent.Fname[:]),
247
			FullPath:      gadgets.FromCString(record.RawSample[unsafe.Offsetof(opensnoopEvent{}.FullFname):]),
248
		}
249

250
		if t.enricher != nil {
251
			t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
252
		}
253

254
		t.eventCallback(&event)
255
	}
256
}
257

258
// --- Registry changes
259

260
func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
261
	t.config.FullPath = gadgetCtx.GadgetParams().Get(ParamFullPath).AsBool()
262
	t.config.Prefixes = gadgetCtx.GadgetParams().Get(ParamPrefixes).AsStringSlice()
263

264
	defer t.close()
265
	if err := t.install(); err != nil {
266
		return fmt.Errorf("installing tracer: %w", err)
267
	}
268

269
	go t.run()
270
	gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
271

272
	return nil
273
}
274

275
func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) {
276
	t.config.MountnsMap = mountnsMap
277
}
278

279
func (t *Tracer) SetEventHandler(handler any) {
280
	nh, ok := handler.(func(ev *types.Event))
281
	if !ok {
282
		panic("event handler invalid")
283
	}
284
	t.eventCallback = nh
285
}
286

287
func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
288
	tracer := &Tracer{
289
		config: &Config{},
290
	}
291
	return tracer, nil
292
}
293

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

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

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

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