inspektor-gadget

Форк
0
272 строки · 6.8 Кб
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
	"os"
23
	"unsafe"
24

25
	"github.com/cilium/ebpf"
26
	"github.com/cilium/ebpf/link"
27
	"github.com/cilium/ebpf/perf"
28

29
	gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
30
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
31
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/exec/types"
32
	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
33
)
34

35
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang -cflags ${CFLAGS} -type event execsnoop ./bpf/execsnoop.bpf.c -- -I./bpf/
36
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang -cflags ${CFLAGS} -type event execsnoopWithCwd ./bpf/execsnoop.bpf.c -- -DWITH_CWD -I./bpf/
37

38
// needs to be kept in sync with execsnoopEvent from execsnoop_bpfel.go without the Args field
39
type execsnoopEventAbbrev struct {
40
	MntnsId    uint64
41
	Timestamp  uint64
42
	Pid        uint32
43
	Ppid       uint32
44
	Uid        uint32
45
	Gid        uint32
46
	Loginuid   uint32
47
	Sessionid  uint32
48
	Retval     int32
49
	ArgsCount  int32
50
	UpperLayer bool
51
	_          [3]byte
52
	ArgsSize   uint32
53
	Comm       [16]uint8
54
}
55

56
// needs to be kept in sync with execsnoopwithcwdEvent from execsnoopwithcwd_bpfel.go without the Args field
57
type execsnoopWithCwdEventAbbrev struct {
58
	MntnsId    uint64
59
	Timestamp  uint64
60
	Pid        uint32
61
	Ppid       uint32
62
	Uid        uint32
63
	Gid        uint32
64
	Loginuid   uint32
65
	Sessionid  uint32
66
	Retval     int32
67
	ArgsCount  int32
68
	UpperLayer bool
69
	_          [3]byte
70
	ArgsSize   uint32
71
	Comm       [16]uint8
72
	Cwd        [4096]uint8
73
}
74

75
type Config struct {
76
	MountnsMap   *ebpf.Map
77
	GetCwd       bool
78
	IgnoreErrors bool
79
}
80

81
type Tracer struct {
82
	config        *Config
83
	enricher      gadgets.DataEnricherByMntNs
84
	eventCallback func(*types.Event)
85

86
	objs      execsnoopObjects
87
	enterLink link.Link
88
	exitLink  link.Link
89
	reader    *perf.Reader
90
}
91

92
func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs,
93
	eventCallback func(*types.Event),
94
) (*Tracer, error) {
95
	t := &Tracer{
96
		config:        config,
97
		enricher:      enricher,
98
		eventCallback: eventCallback,
99
	}
100

101
	if err := t.install(); err != nil {
102
		t.close()
103
		return nil, err
104
	}
105

106
	go t.run()
107

108
	return t, nil
109
}
110

111
// Stop stops the tracer
112
// TODO: Remove after refactoring
113
func (t *Tracer) Stop() {
114
	t.close()
115
}
116

117
func (t *Tracer) close() {
118
	t.enterLink = gadgets.CloseLink(t.enterLink)
119
	t.exitLink = gadgets.CloseLink(t.exitLink)
120

121
	if t.reader != nil {
122
		t.reader.Close()
123
	}
124

125
	t.objs.Close()
126
}
127

128
func (t *Tracer) install() error {
129
	var spec *ebpf.CollectionSpec
130
	var err error
131

132
	if t.config.GetCwd {
133
		spec, err = loadExecsnoopWithCwd()
134
	} else {
135
		spec, err = loadExecsnoop()
136
	}
137
	if err != nil {
138
		return fmt.Errorf("loading ebpf program: %w", err)
139
	}
140

141
	consts := map[string]interface{}{
142
		"ignore_failed": t.config.IgnoreErrors,
143
	}
144

145
	if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil {
146
		return fmt.Errorf("loading ebpf spec: %w", err)
147
	}
148

149
	t.enterLink, err = link.Tracepoint("syscalls", "sys_enter_execve", t.objs.IgExecveE, nil)
150
	if err != nil {
151
		return fmt.Errorf("attaching enter tracepoint: %w", err)
152
	}
153

154
	t.exitLink, err = link.Tracepoint("syscalls", "sys_exit_execve", t.objs.IgExecveX, nil)
155
	if err != nil {
156
		return fmt.Errorf("attaching exit tracepoint: %w", err)
157
	}
158

159
	reader, err := perf.NewReader(t.objs.execsnoopMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
160
	if err != nil {
161
		return fmt.Errorf("creating perf ring buffer: %w", err)
162
	}
163
	t.reader = reader
164

165
	return nil
166
}
167

168
func (t *Tracer) run() {
169
	for {
170
		record, err := t.reader.Read()
171
		if err != nil {
172
			if errors.Is(err, perf.ErrClosed) {
173
				// nothing to do, we're done
174
				return
175
			}
176

177
			msg := fmt.Sprintf("Error reading perf ring buffer: %s", err)
178
			t.eventCallback(types.Base(eventtypes.Err(msg)))
179
			return
180
		}
181

182
		if record.LostSamples > 0 {
183
			msg := fmt.Sprintf("lost %d samples", record.LostSamples)
184
			t.eventCallback(types.Base(eventtypes.Warn(msg)))
185
			continue
186
		}
187

188
		// this works regardless the kind of event because cwd is defined at the end of the
189
		// structure. (Just before args that are handled in a different way below)
190
		bpfEvent := (*execsnoopEventAbbrev)(unsafe.Pointer(&record.RawSample[0]))
191

192
		event := types.Event{
193
			Event: eventtypes.Event{
194
				Type:      eventtypes.NORMAL,
195
				Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
196
			},
197
			Pid:           bpfEvent.Pid,
198
			Ppid:          bpfEvent.Ppid,
199
			Uid:           bpfEvent.Uid,
200
			Gid:           bpfEvent.Gid,
201
			LoginUid:      bpfEvent.Loginuid,
202
			SessionId:     bpfEvent.Sessionid,
203
			UpperLayer:    bpfEvent.UpperLayer,
204
			WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId},
205
			Retval:        int(bpfEvent.Retval),
206
			Comm:          gadgets.FromCString(bpfEvent.Comm[:]),
207
		}
208

209
		argsCount := 0
210
		buf := []byte{}
211
		args := record.RawSample[unsafe.Offsetof(execsnoopEvent{}.Args):]
212

213
		if t.config.GetCwd {
214
			bpfEventWithCwd := (*execsnoopWithCwdEventAbbrev)(unsafe.Pointer(&record.RawSample[0]))
215
			event.Cwd = gadgets.FromCString(bpfEventWithCwd.Cwd[:])
216
			args = record.RawSample[unsafe.Offsetof(execsnoopWithCwdEvent{}.Args):]
217
		}
218

219
		for i := 0; i < int(bpfEvent.ArgsSize) && argsCount < int(bpfEvent.ArgsCount); i++ {
220
			c := args[i]
221
			if c == 0 {
222
				event.Args = append(event.Args, string(buf))
223
				argsCount = 0
224
				buf = []byte{}
225
			} else {
226
				buf = append(buf, c)
227
			}
228
		}
229

230
		if t.enricher != nil {
231
			t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
232
		}
233

234
		t.eventCallback(&event)
235
	}
236
}
237

238
// --- Registry changes
239

240
func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
241
	t.config.GetCwd = gadgetCtx.GadgetParams().Get(ParamCwd).AsBool()
242
	t.config.IgnoreErrors = gadgetCtx.GadgetParams().Get(ParamIgnoreErrors).AsBool()
243

244
	defer t.close()
245
	if err := t.install(); err != nil {
246
		return fmt.Errorf("installing tracer: %w", err)
247
	}
248

249
	go t.run()
250
	gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
251

252
	return nil
253
}
254

255
func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) {
256
	t.config.MountnsMap = mountnsMap
257
}
258

259
func (t *Tracer) SetEventHandler(handler any) {
260
	nh, ok := handler.(func(ev *types.Event))
261
	if !ok {
262
		panic("event handler invalid")
263
	}
264
	t.eventCallback = nh
265
}
266

267
func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
268
	tracer := &Tracer{
269
		config: &Config{},
270
	}
271
	return tracer, nil
272
}
273

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

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

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

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