github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/tcp/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 "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 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/gadgets/trace/tcp/types" 32 eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 33 ) 34 35 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} -no-global-types -type event -type event_type tcptracer ./bpf/tcptracer.bpf.c -- -I./bpf/ 36 37 type Config struct { 38 MountnsMap *ebpf.Map 39 } 40 41 type Tracer struct { 42 config *Config 43 enricher gadgets.DataEnricherByMntNs 44 eventCallback func(*types.Event) 45 46 objs tcptracerObjects 47 48 tcpv4connectEnterLink link.Link 49 tcpv4connectExitLink link.Link 50 tcpv6connectEnterLink link.Link 51 tcpv6connectExitLink link.Link 52 tcpCloseEnterLink link.Link 53 tcpSetStateEnterLink link.Link 54 inetCskAcceptExitLink link.Link 55 56 reader *perf.Reader 57 } 58 59 func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs, 60 eventCallback func(*types.Event), 61 ) (*Tracer, error) { 62 t := &Tracer{ 63 config: config, 64 enricher: enricher, 65 eventCallback: eventCallback, 66 } 67 68 if err := t.install(); err != nil { 69 t.close() 70 return nil, err 71 } 72 73 go t.run() 74 75 return t, nil 76 } 77 78 // Stop stops the tracer 79 // TODO: Remove after refactoring 80 func (t *Tracer) Stop() { 81 t.close() 82 } 83 84 func (t *Tracer) close() { 85 t.tcpv4connectEnterLink = gadgets.CloseLink(t.tcpv4connectEnterLink) 86 t.tcpv4connectExitLink = gadgets.CloseLink(t.tcpv4connectExitLink) 87 t.tcpv6connectEnterLink = gadgets.CloseLink(t.tcpv6connectEnterLink) 88 t.tcpv6connectExitLink = gadgets.CloseLink(t.tcpv6connectExitLink) 89 t.tcpCloseEnterLink = gadgets.CloseLink(t.tcpCloseEnterLink) 90 t.tcpSetStateEnterLink = gadgets.CloseLink(t.tcpSetStateEnterLink) 91 t.inetCskAcceptExitLink = gadgets.CloseLink(t.inetCskAcceptExitLink) 92 93 if t.reader != nil { 94 t.reader.Close() 95 } 96 97 t.objs.Close() 98 } 99 100 func (t *Tracer) install() error { 101 spec, err := loadTcptracer() 102 if err != nil { 103 return fmt.Errorf("loading ebpf program: %w", err) 104 } 105 106 if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, nil, &t.objs); err != nil { 107 return fmt.Errorf("loading ebpf spec: %w", err) 108 } 109 110 t.tcpv4connectEnterLink, err = link.Kprobe("tcp_v4_connect", t.objs.IgTcpV4CoE, nil) 111 if err != nil { 112 return fmt.Errorf("attaching kprobe: %w", err) 113 } 114 115 t.tcpv4connectExitLink, err = link.Kretprobe("tcp_v4_connect", t.objs.IgTcpV4CoX, nil) 116 if err != nil { 117 return fmt.Errorf("attaching kprobe: %w", err) 118 } 119 120 t.tcpv6connectEnterLink, err = link.Kprobe("tcp_v6_connect", t.objs.IgTcpV6CoE, nil) 121 if err != nil { 122 return fmt.Errorf("attaching kprobe: %w", err) 123 } 124 125 t.tcpv6connectExitLink, err = link.Kretprobe("tcp_v6_connect", t.objs.IgTcpV6CoX, nil) 126 if err != nil { 127 return fmt.Errorf("attaching kprobe: %w", err) 128 } 129 130 // TODO: rename function in ebpf program 131 t.tcpCloseEnterLink, err = link.Kprobe("tcp_close", t.objs.IgTcpClose, nil) 132 if err != nil { 133 return fmt.Errorf("attaching kprobe: %w", err) 134 } 135 136 t.tcpSetStateEnterLink, err = link.Kprobe("tcp_set_state", t.objs.IgTcpState, nil) 137 if err != nil { 138 return fmt.Errorf("attaching kprobe: %w", err) 139 } 140 141 t.inetCskAcceptExitLink, err = link.Kretprobe("inet_csk_accept", t.objs.IgTcpAccept, nil) 142 if err != nil { 143 return fmt.Errorf("attaching kprobe: %w", err) 144 } 145 146 reader, err := perf.NewReader(t.objs.tcptracerMaps.Events, gadgets.PerfBufferPages*os.Getpagesize()) 147 if err != nil { 148 return fmt.Errorf("creating perf ring buffer: %w", err) 149 } 150 t.reader = reader 151 152 return nil 153 } 154 155 func (t *Tracer) run() { 156 for { 157 record, err := t.reader.Read() 158 if err != nil { 159 if errors.Is(err, perf.ErrClosed) { 160 // nothing to do, we're done 161 return 162 } 163 164 msg := fmt.Sprintf("Error reading perf ring buffer: %s", err) 165 t.eventCallback(types.Base(eventtypes.Err(msg))) 166 return 167 } 168 169 if record.LostSamples > 0 { 170 msg := fmt.Sprintf("lost %d samples", record.LostSamples) 171 t.eventCallback(types.Base(eventtypes.Warn(msg))) 172 continue 173 } 174 175 bpfEvent := (*tcptracerEvent)(unsafe.Pointer(&record.RawSample[0])) 176 177 ipversion := gadgets.IPVerFromAF(bpfEvent.Af) 178 179 event := types.Event{ 180 Event: eventtypes.Event{ 181 Type: eventtypes.NORMAL, 182 Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp), 183 }, 184 WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId}, 185 Pid: bpfEvent.Pid, 186 Uid: bpfEvent.Uid, 187 Gid: bpfEvent.Gid, 188 Comm: gadgets.FromCString(bpfEvent.Task[:]), 189 SrcEndpoint: eventtypes.L4Endpoint{ 190 L3Endpoint: eventtypes.L3Endpoint{ 191 Addr: gadgets.IPStringFromBytes(bpfEvent.Saddr, ipversion), 192 Version: uint8(ipversion), 193 }, 194 Port: gadgets.Htons(bpfEvent.Sport), 195 }, 196 DstEndpoint: eventtypes.L4Endpoint{ 197 L3Endpoint: eventtypes.L3Endpoint{ 198 Addr: gadgets.IPStringFromBytes(bpfEvent.Daddr, ipversion), 199 Version: uint8(ipversion), 200 }, 201 Port: gadgets.Htons(bpfEvent.Dport), 202 }, 203 IPVersion: ipversion, 204 } 205 206 switch bpfEvent.Type { 207 case tcptracerEventTypeTCP_EVENT_TYPE_CONNECT: 208 event.Operation = "connect" 209 case tcptracerEventTypeTCP_EVENT_TYPE_ACCEPT: 210 event.Operation = "accept" 211 case tcptracerEventTypeTCP_EVENT_TYPE_CLOSE: 212 event.Operation = "close" 213 } 214 215 if t.enricher != nil { 216 t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID) 217 } 218 219 t.eventCallback(&event) 220 } 221 } 222 223 // --- Registry changes 224 225 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 226 defer t.close() 227 if err := t.install(); err != nil { 228 return fmt.Errorf("installing tracer: %w", err) 229 } 230 231 go t.run() 232 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 233 234 return nil 235 } 236 237 func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) { 238 t.config.MountnsMap = mountnsMap 239 } 240 241 func (t *Tracer) SetEventHandler(handler any) { 242 nh, ok := handler.(func(ev *types.Event)) 243 if !ok { 244 panic("event handler invalid") 245 } 246 t.eventCallback = nh 247 } 248 249 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 250 tracer := &Tracer{ 251 config: &Config{}, 252 } 253 return tracer, nil 254 }