github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/advise/seccomp/tracer/tracer.go (about) 1 // Copyright 2019-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 //go:build !withoutebpf 16 17 package tracer 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "sort" 23 24 "github.com/cilium/ebpf" 25 "github.com/cilium/ebpf/link" 26 27 "github.com/inspektor-gadget/inspektor-gadget/pkg/btfgen" 28 containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" 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/utils/syscalls" 32 ) 33 34 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target ${TARGET} -cc clang -cflags ${CFLAGS} seccomp ./bpf/seccomp.bpf.c -- -I./bpf/ 35 36 const ( 37 // Please update these values also in bpf/seccomp-common.h 38 syscallsCount = 500 39 syscallsMapValueFooterSize = 1 40 syscallsMapValueSize = syscallsCount + syscallsMapValueFooterSize 41 ) 42 43 type Tracer struct { 44 objs seccompObjects 45 46 // progLink links the BPF program to the tracepoint. 47 // A reference is kept so it can be closed it explicitly, otherwise 48 // the garbage collector might unlink it via the finalizer at any 49 // moment. 50 progLink link.Link 51 52 // We keep references to mountns of containers we attach to, so we 53 // can collect information afterwards 54 containers map[*containercollection.Container][]string 55 } 56 57 func NewTracer() (*Tracer, error) { 58 t := &Tracer{} 59 60 if err := t.install(); err != nil { 61 t.Close() 62 return nil, err 63 } 64 65 return t, nil 66 } 67 68 func (t *Tracer) install() error { 69 spec, err := loadSeccomp() 70 if err != nil { 71 return fmt.Errorf("loading asset: %w", err) 72 } 73 74 opts := ebpf.CollectionOptions{ 75 Programs: ebpf.ProgramOptions{ 76 KernelTypes: btfgen.GetBTFSpec(), 77 }, 78 } 79 80 if err := spec.LoadAndAssign(&t.objs, &opts); err != nil { 81 return fmt.Errorf("loading ebpf program: %w", err) 82 } 83 84 t.objs.SyscallsPerMntns.Update(uint64(0), [syscallsMapValueSize]byte{}, ebpf.UpdateAny) 85 86 t.progLink, err = link.AttachRawTracepoint(link.RawTracepointOptions{ 87 Name: "sys_enter", 88 Program: t.objs.IgSeccompE, 89 }) 90 if err != nil { 91 return fmt.Errorf("attaching tracepoint: %w", err) 92 } 93 94 return nil 95 } 96 97 func syscallArrToNameList(v []byte) []string { 98 names := []string{} 99 for i, val := range v { 100 if val == 0 { 101 continue 102 } 103 name, ok := syscalls.GetSyscallNameByNumber(i) 104 if !ok { 105 name = fmt.Sprintf("syscall%d", i) 106 } 107 names = append(names, name) 108 } 109 sort.Strings(names) 110 return names 111 } 112 113 func (t *Tracer) Peek(mntns uint64) ([]string, error) { 114 b, err := t.objs.SyscallsPerMntns.LookupBytes(mntns) 115 if err != nil { 116 return nil, fmt.Errorf("looking up the seccomp map: %w", err) 117 } 118 // LookupBytes does not return an error when the entry is not found, so 119 // we need to test b==nil too 120 if b == nil { 121 // The container just hasn't done any syscall 122 return nil, fmt.Errorf("no syscall found") 123 } 124 if len(b) < syscallsCount { 125 return nil, fmt.Errorf("looking up the seccomp map: wrong length: %d", len(b)) 126 } 127 return syscallArrToNameList(b[:syscallsCount]), nil 128 } 129 130 func (t *Tracer) Delete(mntns uint64) { 131 t.objs.SyscallsPerMntns.Delete(mntns) 132 } 133 134 // Close closes the tracer 135 // TODO: Unexport this function when the refactoring is done 136 func (t *Tracer) Close() { 137 t.progLink = gadgets.CloseLink(t.progLink) 138 t.objs.Close() 139 } 140 141 // --- 142 143 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 144 t := &Tracer{ 145 containers: make(map[*containercollection.Container][]string), 146 } 147 return t, nil 148 } 149 150 func (t *Tracer) RunWithResult(gadgetCtx gadgets.GadgetContext) ([]byte, error) { 151 defer t.Close() 152 if err := t.install(); err != nil { 153 return nil, fmt.Errorf("installing tracer: %w", err) 154 } 155 156 // Notice this Tracer starts collecting data for all containers as soon as 157 // it is installed, and uses the attach/detach mechanism to know which 158 // containers to report data from. 159 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 160 161 return t.collectResult() 162 } 163 164 func (t *Tracer) AttachContainer(container *containercollection.Container) error { 165 t.containers[container] = nil 166 return nil 167 } 168 169 func (t *Tracer) DetachContainer(container *containercollection.Container) error { 170 res, err := t.Peek(container.Mntns) 171 if err != nil { 172 t.containers[container] = []string{err.Error()} 173 return nil 174 } 175 t.containers[container] = res 176 return nil 177 } 178 179 func (t *Tracer) collectResult() ([]byte, error) { 180 out := make(map[string][]string) 181 for container, result := range t.containers { 182 out[container.K8s.ContainerName] = result 183 } 184 return json.MarshalIndent(out, "", " ") 185 }