github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/kfilefields/tracer.go (about) 1 // Copyright 2023 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 package kfilefields 16 17 import ( 18 "errors" 19 "fmt" 20 "os" 21 "syscall" 22 23 "github.com/cilium/ebpf" 24 "github.com/cilium/ebpf/link" 25 "golang.org/x/sys/unix" 26 27 "github.com/inspektor-gadget/inspektor-gadget/pkg/btfgen" 28 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 29 ) 30 31 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} -no-global-types filefields ./bpf/filefields.bpf.c -- -I./bpf/ 32 33 type FdType int 34 35 const ( 36 FdTypeSocket FdType = iota 37 FdTypeEbpfProgram 38 ) 39 40 var supportedFdTypesForFOp = map[FdType]struct{}{ 41 FdTypeSocket: {}, 42 FdTypeEbpfProgram: {}, 43 } 44 45 func (fd FdType) String() string { 46 switch fd { 47 case FdTypeSocket: 48 return "socket" 49 case FdTypeEbpfProgram: 50 return "ebpf_program" 51 default: 52 return fmt.Sprintf("unknown(%d)", fd) 53 } 54 } 55 56 type fileFields struct { 57 PrivateData uint64 58 FOp uint64 59 RealInode uint64 60 } 61 62 type Tracer struct { 63 objs filefieldsObjects 64 links []link.Link 65 sock [2]int 66 installed bool 67 } 68 69 func creatAndInstallTracer() (*Tracer, error) { 70 t := &Tracer{} 71 72 if err := t.install(); err != nil { 73 t.close() 74 return nil, fmt.Errorf("installing tracer: %w", err) 75 } 76 77 return t, nil 78 } 79 80 func (t *Tracer) close() { 81 for _, l := range t.links { 82 gadgets.CloseLink(l) 83 } 84 for i := 0; i < len(t.sock); i++ { 85 if t.sock[i] != -1 { 86 unix.Close(t.sock[i]) 87 } 88 } 89 t.objs.Close() 90 } 91 92 func (t *Tracer) install() error { 93 // Create a socket pair 94 var err error 95 t.sock, err = unix.Socketpair(unix.AF_UNIX, unix.SOCK_DGRAM, 0) 96 if err != nil { 97 return fmt.Errorf("creating socket pair: %w", err) 98 } 99 100 // Find the inode of the socket 101 fdFileInfo, err := os.Stat(fmt.Sprintf("/proc/self/fd/%d", t.sock[0])) 102 if err != nil { 103 return fmt.Errorf("reading file info from sock fd %d: %w", t.sock[0], err) 104 } 105 fdStat, ok := fdFileInfo.Sys().(*syscall.Stat_t) 106 if !ok { 107 return errors.New("not a syscall.Stat_t") 108 } 109 fdIno := fdStat.Ino 110 111 // Load ebpf program configured with the socket inode 112 spec, err := loadFilefields() 113 if err != nil { 114 return fmt.Errorf("load ebpf program to read file fields: %w", err) 115 } 116 consts := map[string]interface{}{ 117 "socket_ino": uint64(fdIno), 118 } 119 if err := spec.RewriteConstants(consts); err != nil { 120 return fmt.Errorf("RewriteConstants: %w", err) 121 } 122 123 opts := ebpf.CollectionOptions{ 124 Programs: ebpf.ProgramOptions{ 125 KernelTypes: btfgen.GetBTFSpec(), 126 }, 127 } 128 if err := spec.LoadAndAssign(&t.objs, &opts); err != nil { 129 return fmt.Errorf("loading maps and programs: %w", err) 130 } 131 132 // Attach ebpf programs 133 l, err := link.Kprobe("__scm_send", t.objs.IgScmSndE, nil) 134 if err != nil { 135 return fmt.Errorf("attaching kprobe __scm_send: %w", err) 136 } 137 t.links = append(t.links, l) 138 139 l, err = link.Kretprobe("fget_raw", t.objs.IgFgetX, nil) 140 if err != nil { 141 return fmt.Errorf("attaching kretprobe fget_raw: %w", err) 142 } 143 t.links = append(t.links, l) 144 t.installed = true 145 146 return nil 147 } 148 149 func (t *Tracer) getFdFromType(kind FdType) (int, error) { 150 if !t.installed { 151 return -1, errors.New("tracer not installed") 152 } 153 switch kind { 154 case FdTypeSocket: 155 return t.sock[0], nil 156 case FdTypeEbpfProgram: 157 return t.objs.IgFgetX.FD(), nil 158 default: 159 return -1, fmt.Errorf("unknown fd type %d", kind) 160 } 161 } 162 163 func (t *Tracer) readStructFileFields(fd int) (*fileFields, error) { 164 if !t.installed { 165 return nil, errors.New("tracer not installed") 166 } 167 // Send the fd through the socket with SCM_RIGHTS. 168 // This will trigger the __scm_send kprobe and fget_raw kretprobe 169 buf := make([]byte, 1) 170 err := unix.Sendmsg(t.sock[0], buf, unix.UnixRights(fd), nil, 0) 171 if err != nil { 172 return nil, fmt.Errorf("sending fd: %w", err) 173 } 174 175 var ff fileFields 176 err = t.objs.IgFileFields.Lookup(uint32(0), &ff) 177 if err != nil { 178 return nil, fmt.Errorf("reading file fields: %w", err) 179 } 180 181 return &ff, nil 182 }