github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/open/tracer/tracer.go (about) 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 Err int32 55 Fd uint32 56 Flags int32 57 Mode uint16 58 Comm [16]uint8 59 Fname [255]uint8 60 } 61 62 type Config struct { 63 MountnsMap *ebpf.Map 64 FullPath bool 65 Prefixes []string 66 } 67 68 type Tracer struct { 69 config *Config 70 enricher gadgets.DataEnricherByMntNs 71 eventCallback func(*types.Event) 72 73 objs opensnoopObjects 74 openEnterLink link.Link 75 openAtEnterLink link.Link 76 openExitLink link.Link 77 openAtExitLink link.Link 78 reader *perf.Reader 79 } 80 81 func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs, 82 eventCallback func(*types.Event), 83 ) (*Tracer, error) { 84 t := &Tracer{ 85 config: config, 86 enricher: enricher, 87 eventCallback: eventCallback, 88 } 89 90 if err := t.install(); err != nil { 91 t.close() 92 return nil, err 93 } 94 95 go t.run() 96 97 return t, nil 98 } 99 100 // Stop stops the tracer 101 // TODO: Remove after refactoring 102 func (t *Tracer) Stop() { 103 t.close() 104 } 105 106 func (t *Tracer) close() { 107 t.openEnterLink = gadgets.CloseLink(t.openEnterLink) 108 t.openAtEnterLink = gadgets.CloseLink(t.openAtEnterLink) 109 t.openExitLink = gadgets.CloseLink(t.openExitLink) 110 t.openAtExitLink = gadgets.CloseLink(t.openAtExitLink) 111 112 if t.reader != nil { 113 t.reader.Close() 114 } 115 116 t.objs.Close() 117 } 118 119 func (t *Tracer) install() error { 120 spec, err := loadOpensnoop() 121 if err != nil { 122 return fmt.Errorf("loading ebpf program: %w", err) 123 } 124 125 prefixesNumber := uint32(len(t.config.Prefixes)) 126 prefixesMax := spec.Maps["prefixes"].MaxEntries 127 if prefixesNumber > prefixesMax { 128 return fmt.Errorf("%d maximum prefixes supported, got %d", prefixesMax, prefixesNumber) 129 } 130 131 consts := make(map[string]interface{}) 132 consts["get_full_path"] = t.config.FullPath 133 consts["prefixes_nr"] = prefixesNumber 134 135 for _, prefix := range t.config.Prefixes { 136 var pfx [NAME_MAX]uint8 137 138 bytes := uint32(len(prefix)) 139 if bytes > NAME_MAX { 140 bytes = NAME_MAX 141 } 142 copy(pfx[:], prefix) 143 144 spec.Maps["prefixes"].Contents = append(spec.Maps["prefixes"].Contents, ebpf.MapKV{ 145 // We need to give the exact length of the prefix here. 146 // Otherwise, the kernel will compare until NAME_MAX * CHAR_BIT and there 147 // will never be a match (unless the filename is NAME_MAX long and equals 148 // to the prefix). 149 Key: opensnoopPrefixKey{Prefixlen: bytes * CHAR_BIT, Filename: pfx}, 150 Value: uint8(0), 151 }) 152 } 153 154 if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil { 155 return fmt.Errorf("loading ebpf spec: %w", err) 156 } 157 158 // arm64 does not define the open() syscall, only openat(). 159 if runtime.GOARCH != "arm64" { 160 openEnter, err := link.Tracepoint("syscalls", "sys_enter_open", t.objs.IgOpenE, nil) 161 if err != nil { 162 return fmt.Errorf("attaching tracepoint: %w", err) 163 } 164 t.openEnterLink = openEnter 165 } 166 167 openAtEnter, err := link.Tracepoint("syscalls", "sys_enter_openat", t.objs.IgOpenatE, nil) 168 if err != nil { 169 return fmt.Errorf("attaching tracepoint: %w", err) 170 } 171 t.openAtEnterLink = openAtEnter 172 173 if runtime.GOARCH != "arm64" { 174 openExit, err := link.Tracepoint("syscalls", "sys_exit_open", t.objs.IgOpenX, nil) 175 if err != nil { 176 return fmt.Errorf("attaching tracepoint: %w", err) 177 } 178 t.openExitLink = openExit 179 } 180 181 openAtExit, err := link.Tracepoint("syscalls", "sys_exit_openat", t.objs.IgOpenatX, nil) 182 if err != nil { 183 return fmt.Errorf("attaching tracepoint: %w", err) 184 } 185 t.openAtExitLink = openAtExit 186 187 reader, err := perf.NewReader(t.objs.opensnoopMaps.Events, gadgets.PerfBufferPages*os.Getpagesize()) 188 if err != nil { 189 return fmt.Errorf("creating perf ring buffer: %w", err) 190 } 191 t.reader = reader 192 193 return nil 194 } 195 196 func (t *Tracer) run() { 197 for { 198 record, err := t.reader.Read() 199 if err != nil { 200 if errors.Is(err, perf.ErrClosed) { 201 // nothing to do, we're done 202 return 203 } 204 205 msg := fmt.Sprintf("Error reading perf ring buffer: %s", err) 206 t.eventCallback(types.Base(eventtypes.Err(msg))) 207 return 208 } 209 210 if record.LostSamples > 0 { 211 msg := fmt.Sprintf("lost %d samples", record.LostSamples) 212 t.eventCallback(types.Base(eventtypes.Warn(msg))) 213 continue 214 } 215 216 bpfEvent := (*opensnoopEventAbbrev)(unsafe.Pointer(&record.RawSample[0])) 217 218 mode := fs.FileMode(bpfEvent.Mode) 219 220 event := types.Event{ 221 Event: eventtypes.Event{ 222 Type: eventtypes.NORMAL, 223 Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp), 224 }, 225 WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId}, 226 Pid: bpfEvent.Pid, 227 Uid: bpfEvent.Uid, 228 Gid: bpfEvent.Gid, 229 Comm: gadgets.FromCString(bpfEvent.Comm[:]), 230 Fd: bpfEvent.Fd, 231 Err: bpfEvent.Err, 232 FlagsRaw: bpfEvent.Flags, 233 Flags: DecodeFlags(bpfEvent.Flags), 234 ModeRaw: mode, 235 Mode: mode.String(), 236 Path: gadgets.FromCString(bpfEvent.Fname[:]), 237 FullPath: gadgets.FromCString(record.RawSample[unsafe.Offsetof(opensnoopEvent{}.FullFname):]), 238 } 239 240 if t.enricher != nil { 241 t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID) 242 } 243 244 t.eventCallback(&event) 245 } 246 } 247 248 // --- Registry changes 249 250 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 251 t.config.FullPath = gadgetCtx.GadgetParams().Get(ParamFullPath).AsBool() 252 t.config.Prefixes = gadgetCtx.GadgetParams().Get(ParamPrefixes).AsStringSlice() 253 254 defer t.close() 255 if err := t.install(); err != nil { 256 return fmt.Errorf("installing tracer: %w", err) 257 } 258 259 go t.run() 260 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 261 262 return nil 263 } 264 265 func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) { 266 t.config.MountnsMap = mountnsMap 267 } 268 269 func (t *Tracer) SetEventHandler(handler any) { 270 nh, ok := handler.(func(ev *types.Event)) 271 if !ok { 272 panic("event handler invalid") 273 } 274 t.eventCallback = nh 275 } 276 277 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 278 tracer := &Tracer{ 279 config: &Config{}, 280 } 281 return tracer, nil 282 }