github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/tcpretrans/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 "unsafe" 24 25 "github.com/cilium/ebpf" 26 "github.com/cilium/ebpf/link" 27 "github.com/cilium/ebpf/perf" 28 29 "github.com/inspektor-gadget/inspektor-gadget/pkg/btfgen" 30 gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context" 31 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 32 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/tcpretrans/types" 33 "github.com/inspektor-gadget/inspektor-gadget/pkg/socketenricher" 34 "github.com/inspektor-gadget/inspektor-gadget/pkg/tcpbits" 35 eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 36 ) 37 38 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} -no-global-types -type event -type type tcpretrans ./bpf/tcpretrans.bpf.c -- -I./bpf/ 39 40 type Tracer struct { 41 socketEnricherMap *ebpf.Map 42 43 eventCallback func(*types.Event) 44 45 objs tcpretransObjects 46 retransmitSkbLink link.Link 47 lossSkbLink link.Link 48 reader *perf.Reader 49 } 50 51 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 52 return &Tracer{}, nil 53 } 54 55 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 56 defer t.close() 57 if err := t.install(); err != nil { 58 return fmt.Errorf("installing tracer: %w", err) 59 } 60 61 go t.run() 62 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 63 64 return nil 65 } 66 67 func (t *Tracer) SetEventHandler(handler any) { 68 nh, ok := handler.(func(ev *types.Event)) 69 if !ok { 70 panic("event handler invalid") 71 } 72 t.eventCallback = nh 73 } 74 75 func (t *Tracer) SetSocketEnricherMap(m *ebpf.Map) { 76 t.socketEnricherMap = m 77 } 78 79 func (t *Tracer) close() { 80 t.retransmitSkbLink = gadgets.CloseLink(t.retransmitSkbLink) 81 t.lossSkbLink = gadgets.CloseLink(t.lossSkbLink) 82 83 if t.reader != nil { 84 t.reader.Close() 85 } 86 87 t.objs.Close() 88 } 89 90 func (t *Tracer) install() error { 91 var err error 92 93 spec, err := loadTcpretrans() 94 if err != nil { 95 return fmt.Errorf("loading ebpf program: %w", err) 96 } 97 98 gadgets.FixBpfKtimeGetBootNs(spec.Programs) 99 100 opts := ebpf.CollectionOptions{ 101 Programs: ebpf.ProgramOptions{ 102 KernelTypes: btfgen.GetBTFSpec(), 103 }, 104 } 105 106 mapReplacements := map[string]*ebpf.Map{} 107 mapReplacements[socketenricher.SocketsMapName] = t.socketEnricherMap 108 opts.MapReplacements = mapReplacements 109 110 if err := spec.LoadAndAssign(&t.objs, &opts); err != nil { 111 return fmt.Errorf("loading ebpf program: %w", err) 112 } 113 114 t.retransmitSkbLink, err = link.Tracepoint("tcp", "tcp_retransmit_skb", t.objs.IgTcpretrans, nil) 115 if err != nil { 116 return fmt.Errorf("attaching tracepoint tcp_retransmit_skb: %w", err) 117 } 118 119 t.lossSkbLink, err = link.Kprobe("tcp_send_loss_probe", t.objs.IgTcplossprobe, nil) 120 if err != nil { 121 return fmt.Errorf("attaching kprobe tcp_send_loss_probe: %w", err) 122 } 123 124 reader, err := perf.NewReader(t.objs.tcpretransMaps.Events, gadgets.PerfBufferPages*os.Getpagesize()) 125 if err != nil { 126 return fmt.Errorf("creating perf ring buffer: %w", err) 127 } 128 t.reader = reader 129 130 return nil 131 } 132 133 func (t *Tracer) run() { 134 for { 135 record, err := t.reader.Read() 136 if err != nil { 137 if errors.Is(err, perf.ErrClosed) { 138 // nothing to do, we're done 139 return 140 } 141 142 msg := fmt.Sprintf("reading perf ring buffer: %s", err) 143 t.eventCallback(types.Base(eventtypes.Err(msg))) 144 return 145 } 146 147 if record.LostSamples > 0 { 148 msg := fmt.Sprintf("lost %d samples", record.LostSamples) 149 t.eventCallback(types.Base(eventtypes.Warn(msg))) 150 continue 151 } 152 153 bpfEvent := (*tcpretransEvent)(unsafe.Pointer(&record.RawSample[0])) 154 155 ipversion := gadgets.IPVerFromAF(bpfEvent.Af) 156 157 typ := "unknown" 158 switch bpfEvent.Type { 159 case tcpretransTypeRETRANS: 160 typ = "RETRANS" 161 case tcpretransTypeLOSS: 162 typ = "LOSS" 163 } 164 165 event := types.Event{ 166 Event: eventtypes.Event{ 167 Type: eventtypes.NORMAL, 168 Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp), 169 }, 170 WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.ProcSocket.MountNsId}, 171 WithNetNsID: eventtypes.WithNetNsID{NetNsID: uint64(bpfEvent.Netns)}, 172 Pid: bpfEvent.ProcSocket.Pid, 173 Uid: bpfEvent.ProcSocket.Uid, 174 Gid: bpfEvent.ProcSocket.Gid, 175 Comm: gadgets.FromCString(bpfEvent.ProcSocket.Task[:]), 176 IPVersion: ipversion, 177 SrcEndpoint: eventtypes.L4Endpoint{ 178 L3Endpoint: eventtypes.L3Endpoint{ 179 Addr: gadgets.IPStringFromBytes(bpfEvent.Saddr, ipversion), 180 Version: uint8(ipversion), 181 }, 182 Port: gadgets.Htons(bpfEvent.Sport), 183 }, 184 DstEndpoint: eventtypes.L4Endpoint{ 185 L3Endpoint: eventtypes.L3Endpoint{ 186 Addr: gadgets.IPStringFromBytes(bpfEvent.Daddr, ipversion), 187 Version: uint8(ipversion), 188 }, 189 Port: gadgets.Htons(bpfEvent.Dport), 190 }, 191 State: tcpbits.TCPState(bpfEvent.State), 192 Tcpflags: tcpbits.TCPFlags(bpfEvent.Tcpflags), 193 Type: typ, 194 } 195 196 t.eventCallback(&event) 197 } 198 }