github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/operators/ebpf/snapshotter.go (about) 1 // Copyright 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 package ebpfoperator 16 17 import ( 18 "errors" 19 "fmt" 20 "io" 21 "strings" 22 23 "github.com/cilium/ebpf" 24 "github.com/cilium/ebpf/btf" 25 "github.com/cilium/ebpf/link" 26 27 "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource" 28 metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1" 29 "github.com/inspektor-gadget/inspektor-gadget/pkg/netnsenter" 30 bpfiterns "github.com/inspektor-gadget/inspektor-gadget/pkg/utils/bpf-iter-ns" 31 ) 32 33 type linkSnapshotter struct { 34 link *link.Iter 35 typ string 36 } 37 38 type Snapshotter struct { 39 metadatav1.Snapshotter 40 41 ds datasource.DataSource 42 accessor datasource.FieldAccessor 43 netns datasource.FieldAccessor 44 45 // iterators is a list of iterators that this snapshotter needs to run to 46 // get the data. This information is gathered from the snapshotter 47 // definition in the eBPF program. 48 iterators map[string]struct{} 49 50 // links is a map of iterators to their links. Links are created when the 51 // iterator is attached to the kernel. 52 links map[string]*linkSnapshotter 53 } 54 55 func (i *ebpfInstance) parseSnapshotterPrograms(programs []string) (map[string]struct{}, error) { 56 iterators := make(map[string]struct{}, len(programs)) 57 58 for _, program := range programs { 59 if program == "" { 60 return nil, errors.New("empty program name") 61 } 62 63 i.logger.Debugf("> program %q", program) 64 65 // Check if the program is in the eBPF object 66 p, ok := i.collectionSpec.Programs[program] 67 if !ok { 68 return nil, fmt.Errorf("program %q not found in eBPF object", program) 69 } 70 71 if p.Type != ebpf.Tracing || !strings.HasPrefix(p.SectionName, "iter/") { 72 return nil, fmt.Errorf("invalid program %q: expecting type %q and section name prefix \"iter/\", got %q and %q", 73 program, ebpf.Tracing, p.Type, p.SectionName) 74 } 75 76 iterators[program] = struct{}{} 77 } 78 79 return iterators, nil 80 } 81 82 func (i *ebpfInstance) populateSnapshotter(t btf.Type, varName string) error { 83 i.logger.Debugf("populating snapshotter %q", varName) 84 85 parts := strings.Split(varName, typeSplitter) 86 if len(parts) < 3 { 87 // At least one program is required 88 return fmt.Errorf("invalid snapshotter definition, expected format: <name>___<structName>___<program1>___...___<programN>, got %q", 89 varName) 90 } 91 92 name := parts[0] 93 structName := parts[1] 94 95 i.logger.Debugf("> name : %q", name) 96 i.logger.Debugf("> struct name: %q", structName) 97 98 snapConfig := i.config.Sub("snapshotters." + name) 99 if snapConfig != nil { 100 if configStructName := snapConfig.GetString("structName"); configStructName != "" && configStructName != structName { 101 return fmt.Errorf("validating snapshotter %q: structName %q in eBPF program does not match %q from metadata file", 102 name, configStructName, structName) 103 } 104 i.logger.Debugf("> successfully validated with metadata") 105 } 106 107 iterators, err := i.parseSnapshotterPrograms(parts[2:]) 108 if err != nil { 109 return fmt.Errorf("parsing snapshotter %q programs: %w", name, err) 110 } 111 112 if _, ok := i.snapshotters[name]; ok { 113 i.logger.Debugf("snapshotter %q already defined, skipping", name) 114 return nil 115 } 116 117 var btfStruct *btf.Struct 118 if err := i.collectionSpec.Types.TypeByName(structName, &btfStruct); err != nil { 119 return fmt.Errorf("finding struct %q in eBPF object: %w", structName, err) 120 } 121 122 i.logger.Debugf("adding snapshotter %q", name) 123 i.snapshotters[name] = &Snapshotter{ 124 Snapshotter: metadatav1.Snapshotter{ 125 StructName: btfStruct.Name, 126 }, 127 iterators: iterators, 128 links: make(map[string]*linkSnapshotter), 129 } 130 131 err = i.populateStructDirect(btfStruct) 132 if err != nil { 133 return fmt.Errorf("populating struct %q for snapshotter %q: %w", btfStruct.Name, name, err) 134 } 135 136 return nil 137 } 138 139 func (i *ebpfInstance) runSnapshotters() error { 140 for sName, snapshotter := range i.snapshotters { 141 i.logger.Debugf("Running snapshotter %q", sName) 142 143 for pName, l := range snapshotter.links { 144 i.logger.Debugf("Running iterator %q", pName) 145 switch l.typ { 146 case "task": 147 buf, err := bpfiterns.Read(l.link) 148 if err != nil { 149 return fmt.Errorf("reading iterator %q: %w", pName, err) 150 } 151 152 size := snapshotter.accessor.Size() 153 if uint32(len(buf))%size != 0 { 154 return fmt.Errorf("iter %q returned an invalid buffer's size %d, expected multiple of %d", 155 pName, len(buf), size) 156 } 157 158 for i := uint32(0); i < uint32(len(buf)); i += size { 159 data := snapshotter.ds.NewData() 160 snapshotter.accessor.Set(data, buf[i:i+size]) 161 snapshotter.ds.EmitAndRelease(data) 162 } 163 case "tcp", "udp": 164 visitedNetNs := make(map[uint64]struct{}) 165 for _, container := range i.containers { 166 _, visited := visitedNetNs[container.Netns] 167 if visited { 168 continue 169 } 170 visitedNetNs[container.Netns] = struct{}{} 171 172 err := netnsenter.NetnsEnter(int(container.Pid), func() error { 173 reader, err := l.link.Open() 174 if err != nil { 175 return err 176 } 177 defer reader.Close() 178 179 buf, err := io.ReadAll(reader) 180 if err != nil { 181 return fmt.Errorf("reading iterator %q: %w", pName, err) 182 } 183 184 size := snapshotter.accessor.Size() 185 if uint32(len(buf))%size != 0 { 186 return fmt.Errorf("iter %q returned an invalid buffer's size %d, expected multiple of %d", 187 pName, len(buf), size) 188 } 189 190 for i := uint32(0); i < uint32(len(buf)); i += size { 191 data := snapshotter.ds.NewData() 192 snapshotter.accessor.Set(data, buf[i:i+size]) 193 194 // TODO: this isn't ideal; make DS reserve memory / clean on demand 195 // instead of allocating in here - or: reserve those 8 bytes in eBPF 196 snapshotter.netns.Set(data, make([]byte, 8)) 197 snapshotter.netns.PutUint64(data, container.Netns) 198 199 snapshotter.ds.EmitAndRelease(data) 200 } 201 202 return nil 203 }) 204 if err != nil { 205 return fmt.Errorf("entering container %q's netns to run iterator %q: %w", 206 container.Runtime.RuntimeName, pName, err) 207 } 208 } 209 } 210 } 211 } 212 return nil 213 }