github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/tcpdrop/tracer/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 //go:build !withoutebpf 16 17 package tracer 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 "strings" 24 "unsafe" 25 26 "github.com/cilium/ebpf" 27 "github.com/cilium/ebpf/btf" 28 "github.com/cilium/ebpf/link" 29 "github.com/cilium/ebpf/perf" 30 31 "github.com/inspektor-gadget/inspektor-gadget/pkg/btfgen" 32 gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context" 33 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 34 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/tcpdrop/types" 35 "github.com/inspektor-gadget/inspektor-gadget/pkg/socketenricher" 36 "github.com/inspektor-gadget/inspektor-gadget/pkg/tcpbits" 37 eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 38 ) 39 40 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} -no-global-types -type event tcpdrop ./bpf/tcpdrop.bpf.c -- -I./bpf/ 41 type Tracer struct { 42 socketEnricherMap *ebpf.Map 43 dropReasons map[int]string 44 45 eventCallback func(*types.Event) 46 47 objs tcpdropObjects 48 kfreeSkbLink link.Link 49 reader *perf.Reader 50 } 51 52 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 53 return &Tracer{}, nil 54 } 55 56 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 57 defer t.close() 58 if err := t.install(); err != nil { 59 return fmt.Errorf("installing tracer: %w", err) 60 } 61 62 go t.run() 63 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 64 65 return nil 66 } 67 68 func (t *Tracer) SetEventHandler(handler any) { 69 nh, ok := handler.(func(ev *types.Event)) 70 if !ok { 71 panic("event handler invalid") 72 } 73 t.eventCallback = nh 74 } 75 76 func (t *Tracer) SetSocketEnricherMap(m *ebpf.Map) { 77 t.socketEnricherMap = m 78 } 79 80 func (t *Tracer) close() { 81 t.kfreeSkbLink = gadgets.CloseLink(t.kfreeSkbLink) 82 83 if t.reader != nil { 84 t.reader.Close() 85 } 86 87 t.objs.Close() 88 } 89 90 func (t *Tracer) loadDropReasons() error { 91 btfSpec, err := btf.LoadKernelSpec() 92 if err != nil { 93 return fmt.Errorf("loading kernel spec: %w", err) 94 } 95 96 t.dropReasons = make(map[int]string) 97 enum := &btf.Enum{} 98 err = btfSpec.TypeByName("skb_drop_reason", &enum) 99 if err != nil { 100 return fmt.Errorf("looking up skb_drop_reason enum: %w", err) 101 } 102 for _, v := range enum.Values { 103 str := v.Name 104 str = strings.TrimPrefix(str, "SKB_DROP_REASON_") 105 str = strings.TrimPrefix(str, "SKB_") 106 107 t.dropReasons[int(v.Value)] = str 108 } 109 110 return nil 111 } 112 113 func (t *Tracer) install() error { 114 err := t.loadDropReasons() 115 if err != nil { 116 return err 117 } 118 119 spec, err := loadTcpdrop() 120 if err != nil { 121 return fmt.Errorf("loading ebpf program: %w", err) 122 } 123 124 gadgets.FixBpfKtimeGetBootNs(spec.Programs) 125 126 opts := ebpf.CollectionOptions{ 127 Programs: ebpf.ProgramOptions{ 128 KernelTypes: btfgen.GetBTFSpec(), 129 }, 130 } 131 132 mapReplacements := map[string]*ebpf.Map{} 133 mapReplacements[socketenricher.SocketsMapName] = t.socketEnricherMap 134 opts.MapReplacements = mapReplacements 135 136 if err := spec.LoadAndAssign(&t.objs, &opts); err != nil { 137 return fmt.Errorf("loading ebpf program: %w", err) 138 } 139 140 t.kfreeSkbLink, err = link.Tracepoint("skb", "kfree_skb", t.objs.IgTcpdrop, nil) 141 if err != nil { 142 return fmt.Errorf("attaching tracepoint kfree_skb: %w", err) 143 } 144 145 reader, err := perf.NewReader(t.objs.tcpdropMaps.Events, gadgets.PerfBufferPages*os.Getpagesize()) 146 if err != nil { 147 return fmt.Errorf("creating perf ring buffer: %w", err) 148 } 149 t.reader = reader 150 151 return nil 152 } 153 154 func (t *Tracer) run() { 155 for { 156 record, err := t.reader.Read() 157 if err != nil { 158 if errors.Is(err, perf.ErrClosed) { 159 // nothing to do, we're done 160 return 161 } 162 163 msg := fmt.Sprintf("reading perf ring buffer: %s", err) 164 t.eventCallback(types.Base(eventtypes.Err(msg))) 165 return 166 } 167 168 if record.LostSamples > 0 { 169 msg := fmt.Sprintf("lost %d samples", record.LostSamples) 170 t.eventCallback(types.Base(eventtypes.Warn(msg))) 171 continue 172 } 173 174 bpfEvent := (*tcpdropEvent)(unsafe.Pointer(&record.RawSample[0])) 175 176 reason, err := t.lookupDropReason(int(bpfEvent.Reason)) 177 if err != nil { 178 msg := fmt.Sprintf("looking up drop reason: %s", err) 179 t.eventCallback(types.Base(eventtypes.Err(msg))) 180 continue 181 } 182 183 ipversion := gadgets.IPVerFromAF(bpfEvent.Af) 184 185 event := types.Event{ 186 Event: eventtypes.Event{ 187 Type: eventtypes.NORMAL, 188 Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp), 189 }, 190 WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.ProcSocket.MountNsId}, 191 WithNetNsID: eventtypes.WithNetNsID{NetNsID: uint64(bpfEvent.Netns)}, 192 Pid: bpfEvent.ProcSocket.Pid, 193 Uid: bpfEvent.ProcSocket.Uid, 194 Gid: bpfEvent.ProcSocket.Gid, 195 Comm: gadgets.FromCString(bpfEvent.ProcSocket.Task[:]), 196 197 SrcEndpoint: eventtypes.L4Endpoint{ 198 L3Endpoint: eventtypes.L3Endpoint{ 199 Addr: gadgets.IPStringFromBytes(bpfEvent.Saddr, ipversion), 200 Version: uint8(ipversion), 201 }, 202 Port: gadgets.Htons(bpfEvent.Sport), 203 }, 204 DstEndpoint: eventtypes.L4Endpoint{ 205 L3Endpoint: eventtypes.L3Endpoint{ 206 Addr: gadgets.IPStringFromBytes(bpfEvent.Daddr, ipversion), 207 Version: uint8(ipversion), 208 }, 209 Port: gadgets.Htons(bpfEvent.Dport), 210 }, 211 State: tcpbits.TCPState(bpfEvent.State), 212 Tcpflags: tcpbits.TCPFlags(bpfEvent.Tcpflags), 213 Reason: reason, 214 IPVersion: ipversion, 215 } 216 217 t.eventCallback(&event) 218 } 219 } 220 221 func (t *Tracer) lookupDropReason(reason int) (string, error) { 222 if ret, ok := t.dropReasons[reason]; ok { 223 return ret, nil 224 } 225 return "", fmt.Errorf("unknown drop reason: %d", reason) 226 }