1
// Copyright 2019-2024 The Inspektor Gadget authors
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
7
// http://www.apache.org/licenses/LICENSE-2.0
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.
15
//go:build !withoutebpf
27
"github.com/cilium/ebpf"
28
"github.com/cilium/ebpf/link"
29
"github.com/cilium/ebpf/perf"
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"
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/
40
// Keep in sync with opensnoop.h.
42
// Keep in sync with opensnoop.bpf.c.
46
// needs to be kept in sync with opensnoopEvent from opensnoop_bpfel.go without the FullFname field
47
type opensnoopEventAbbrev struct {
69
enricher gadgets.DataEnricherByMntNs
70
eventCallback func(*types.Event)
73
openEnterLink link.Link
74
openAtEnterLink link.Link
75
openExitLink link.Link
76
openAtExitLink link.Link
80
func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs,
81
eventCallback func(*types.Event),
86
eventCallback: eventCallback,
89
if err := t.install(); err != nil {
99
// Stop stops the tracer
100
// TODO: Remove after refactoring
101
func (t *Tracer) Stop() {
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)
118
func (t *Tracer) install() error {
119
spec, err := loadOpensnoop()
121
return fmt.Errorf("loading ebpf program: %w", err)
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)
130
consts := make(map[string]interface{})
131
consts["get_full_path"] = t.config.FullPath
132
consts["prefixes_nr"] = prefixesNumber
134
for _, prefix := range t.config.Prefixes {
135
var pfx [NAME_MAX]uint8
137
bytes := uint32(len(prefix))
138
if bytes > NAME_MAX {
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
148
Key: opensnoopPrefixKey{Prefixlen: bytes * CHAR_BIT, Filename: pfx},
153
if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil {
154
return fmt.Errorf("loading ebpf spec: %w", err)
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)
161
return fmt.Errorf("attaching tracepoint: %w", err)
163
t.openEnterLink = openEnter
166
openAtEnter, err := link.Tracepoint("syscalls", "sys_enter_openat", t.objs.IgOpenatE, nil)
168
return fmt.Errorf("attaching tracepoint: %w", err)
170
t.openAtEnterLink = openAtEnter
172
if runtime.GOARCH != "arm64" {
173
openExit, err := link.Tracepoint("syscalls", "sys_exit_open", t.objs.IgOpenX, nil)
175
return fmt.Errorf("attaching tracepoint: %w", err)
177
t.openExitLink = openExit
180
openAtExit, err := link.Tracepoint("syscalls", "sys_exit_openat", t.objs.IgOpenatX, nil)
182
return fmt.Errorf("attaching tracepoint: %w", err)
184
t.openAtExitLink = openAtExit
186
reader, err := perf.NewReader(t.objs.opensnoopMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
188
return fmt.Errorf("creating perf ring buffer: %w", err)
195
func (t *Tracer) run() {
197
record, err := t.reader.Read()
199
if errors.Is(err, perf.ErrClosed) {
200
// nothing to do, we're done
204
msg := fmt.Sprintf("Error reading perf ring buffer: %s", err)
205
t.eventCallback(types.Base(eventtypes.Err(msg)))
209
if record.LostSamples > 0 {
210
msg := fmt.Sprintf("lost %d samples", record.LostSamples)
211
t.eventCallback(types.Base(eventtypes.Warn(msg)))
215
bpfEvent := (*opensnoopEventAbbrev)(unsafe.Pointer(&record.RawSample[0]))
217
ret := int(bpfEvent.Ret)
227
mode := fs.FileMode(bpfEvent.Mode)
229
event := types.Event{
230
Event: eventtypes.Event{
231
Type: eventtypes.NORMAL,
232
Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
234
WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId},
238
Comm: gadgets.FromCString(bpfEvent.Comm[:]),
242
FlagsRaw: bpfEvent.Flags,
243
Flags: DecodeFlags(bpfEvent.Flags),
246
Path: gadgets.FromCString(bpfEvent.Fname[:]),
247
FullPath: gadgets.FromCString(record.RawSample[unsafe.Offsetof(opensnoopEvent{}.FullFname):]),
250
if t.enricher != nil {
251
t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
254
t.eventCallback(&event)
258
// --- Registry changes
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()
265
if err := t.install(); err != nil {
266
return fmt.Errorf("installing tracer: %w", err)
270
gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
275
func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) {
276
t.config.MountnsMap = mountnsMap
279
func (t *Tracer) SetEventHandler(handler any) {
280
nh, ok := handler.(func(ev *types.Event))
282
panic("event handler invalid")
287
func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {