github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/bind/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 "github.com/vishvananda/netlink" 29 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/bind/types" 33 eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 34 ) 35 36 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} -type bind_event bindsnoop ./bpf/bindsnoop.bpf.c -- -I./bpf/ 37 38 type Config struct { 39 MountnsMap *ebpf.Map 40 TargetPid int32 41 TargetPorts []uint16 42 IgnoreErrors bool 43 } 44 45 type Tracer struct { 46 config *Config 47 enricher gadgets.DataEnricherByMntNs 48 eventCallback func(*types.Event) 49 50 objs bindsnoopObjects 51 ipv4Entry link.Link 52 ipv4Exit link.Link 53 ipv6Entry link.Link 54 ipv6Exit link.Link 55 reader *perf.Reader 56 } 57 58 func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs, 59 eventCallback func(*types.Event), 60 ) (*Tracer, error) { 61 t := &Tracer{ 62 config: config, 63 enricher: enricher, 64 eventCallback: eventCallback, 65 } 66 67 if err := t.install(); err != nil { 68 t.close() 69 return nil, err 70 } 71 72 go t.run() 73 74 return t, nil 75 } 76 77 // Stop stops the tracer 78 // TODO: Remove after refactoring 79 func (t *Tracer) Stop() { 80 t.close() 81 } 82 83 func (t *Tracer) close() { 84 t.ipv4Entry = gadgets.CloseLink(t.ipv4Entry) 85 t.ipv4Exit = gadgets.CloseLink(t.ipv4Exit) 86 t.ipv6Entry = gadgets.CloseLink(t.ipv6Entry) 87 t.ipv6Exit = gadgets.CloseLink(t.ipv6Exit) 88 89 if t.reader != nil { 90 t.reader.Close() 91 } 92 93 t.objs.Close() 94 } 95 96 func (t *Tracer) install() error { 97 spec, err := loadBindsnoop() 98 if err != nil { 99 return fmt.Errorf("loading ebpf program: %w", err) 100 } 101 102 filterByPort := false 103 if len(t.config.TargetPorts) > 0 { 104 filterByPort = true 105 106 m := spec.Maps["ports"] 107 for _, port := range t.config.TargetPorts { 108 m.Contents = append(m.Contents, ebpf.MapKV{Key: port, Value: port}) 109 } 110 } 111 112 consts := map[string]interface{}{ 113 "target_pid": t.config.TargetPid, 114 "filter_by_port": filterByPort, 115 "ignore_errors": t.config.IgnoreErrors, 116 } 117 118 if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil { 119 return fmt.Errorf("loading ebpf spec: %w", err) 120 } 121 122 t.ipv4Entry, err = link.Kprobe("inet_bind", t.objs.IgBindIpv4E, nil) 123 if err != nil { 124 return fmt.Errorf("attaching ipv4 kprobe: %w", err) 125 } 126 127 t.ipv4Exit, err = link.Kretprobe("inet_bind", t.objs.IgBindIpv4X, nil) 128 if err != nil { 129 return fmt.Errorf("attaching ipv4 kprobe: %w", err) 130 } 131 132 t.ipv6Entry, err = link.Kprobe("inet6_bind", t.objs.IgBindIpv6E, nil) 133 if err != nil { 134 return fmt.Errorf("attaching ipv6 kprobe: %w", err) 135 } 136 137 t.ipv6Exit, err = link.Kretprobe("inet6_bind", t.objs.IgBindIpv6X, nil) 138 if err != nil { 139 return fmt.Errorf("attaching ipv6 kprobe: %w", err) 140 } 141 142 t.reader, err = perf.NewReader(t.objs.bindsnoopMaps.Events, gadgets.PerfBufferPages*os.Getpagesize()) 143 if err != nil { 144 return fmt.Errorf("creating perf ring buffer: %w", err) 145 } 146 147 return nil 148 } 149 150 // optionsToString translates options bitfield to a string containing a letter 151 // if the option is set or a dot. 152 // It is a translation of opts2array added in this commit of kinvolk/bcc: 153 // 9621f010e33c ("tools/bindsnoop: add support for --json") 154 func optionsToString(options uint8) string { 155 ret := "" 156 bit := uint8(1) 157 158 for _, option := range []string{"F", "T", "N", "R", "r"} { 159 if (options & bit) != 0 { 160 ret = option + ret 161 } else { 162 ret = "." + ret 163 } 164 bit <<= 1 165 } 166 167 return ret 168 } 169 170 // Taken from: 171 // https://elixir.bootlin.com/linux/v5.16.10/source/include/uapi/linux/in.h#L28 172 var socketProtocol = map[uint16]string{ 173 0: "IP", // Dummy protocol for TCP 174 1: "ICMP", // Internet Control Message Protocol 175 2: "IGMP", // Internet Group Management Protocol 176 4: "IPIP", // IPIP tunnels (older KA9Q tunnels use 94) 177 6: "TCP", // Transmission Control Protocol 178 8: "EGP", // Exterior Gateway Protocol 179 12: "PUP", // PUP protocol 180 17: "UDP", // User Datagram Protocol 181 22: "IDP", // XNS IDP protocol 182 29: "TP", // SO Transport Protocol Class 4 183 33: "DCCP", // Datagram Congestion Control Protocol 184 41: "IPV6", // IPv6-in-IPv4 tunnelling 185 46: "RSVP", // RSVP Protocol 186 47: "GRE", // Cisco GRE tunnels (rfc 1701,1702) 187 50: "ESP", // Encapsulation Security Payload protocol 188 51: "AH", // Authentication Header protocol 189 92: "MTP", // Multicast Transport Protocol 190 94: "BEETPH", // IP option pseudo header for BEET 191 98: "ENCAP", // Encapsulation Header 192 103: "PIM", // Protocol Independent Multicast 193 108: "COMP", // Compression Header Protocol 194 132: "SCTP", // Stream Control Transport Protocol 195 136: "UDPLITE", // UDP-Lite (RFC 3828) 196 137: "MPLS", // MPLS in IP (RFC 4023) 197 143: "ETHERNET", // Ethernet-within-IPv6 Encapsulation 198 255: "RAW", // Raw IP packets 199 262: "MPTCP", // Multipath TCP connection 200 } 201 202 // protocolToString translates a kernel protocol enum value to the protocol 203 // name. 204 func protocolToString(protocol uint16) string { 205 protocolString, ok := socketProtocol[protocol] 206 if !ok { 207 protocolString = "UNKNOWN" 208 } 209 210 return protocolString 211 } 212 213 func (t *Tracer) run() { 214 for { 215 record, err := t.reader.Read() 216 if err != nil { 217 if errors.Is(err, perf.ErrClosed) { 218 // nothing to do, we're done 219 return 220 } 221 222 msg := fmt.Sprintf("Error reading perf ring buffer: %s", err) 223 t.eventCallback(types.Base(eventtypes.Err(msg))) 224 return 225 } 226 227 if record.LostSamples > 0 { 228 msg := fmt.Sprintf("lost %d samples", record.LostSamples) 229 t.eventCallback(types.Base(eventtypes.Warn(msg))) 230 continue 231 } 232 233 bpfEvent := (*bindsnoopBindEvent)(unsafe.Pointer(&record.RawSample[0])) 234 235 interfaceString := "" 236 interfaceNum := int(bpfEvent.BoundDevIf) 237 if interfaceNum != 0 { 238 // It does exist a net link which index is 0. 239 // But eBPF bindsnoop code often gives 0 as interface number: 240 // https://github.com/iovisor/bcc/blob/63618552f81a2631990eff59fd7460802c58c30b/tools/bindsnoop_example.txt#L16 241 // So, we only use this function if interface number is different than 0. 242 interf, err := netlink.LinkByIndex(interfaceNum) 243 if err != nil { 244 msg := fmt.Sprintf("Cannot get net interface for %d : %s", interfaceNum, err) 245 t.eventCallback(types.Base(eventtypes.Err(msg))) 246 return 247 } 248 249 interfaceString = interf.Attrs().Name 250 } 251 252 addr := gadgets.IPStringFromBytes(bpfEvent.Addr, int(bpfEvent.Ver)) 253 254 event := types.Event{ 255 Event: eventtypes.Event{ 256 Type: eventtypes.NORMAL, 257 Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp), 258 }, 259 Pid: bpfEvent.Pid, 260 Protocol: protocolToString(bpfEvent.Proto), 261 Addr: addr, 262 Port: bpfEvent.Port, 263 Options: optionsToString(bpfEvent.Opts), 264 Interface: interfaceString, 265 Comm: gadgets.FromCString(bpfEvent.Task[:]), 266 WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MountNsId}, 267 Uid: bpfEvent.Uid, 268 Gid: bpfEvent.Gid, 269 } 270 271 if t.enricher != nil { 272 t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID) 273 } 274 275 t.eventCallback(&event) 276 } 277 } 278 279 // --- Registry changes 280 281 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 282 params := gadgetCtx.GadgetParams() 283 t.config.TargetPid = params.Get(ParamPID).AsInt32() 284 t.config.TargetPorts = params.Get(ParamPorts).AsUint16Slice() 285 t.config.IgnoreErrors = params.Get(ParamIgnoreErrors).AsBool() 286 287 defer t.close() 288 if err := t.install(); err != nil { 289 return fmt.Errorf("installing tracer: %w", err) 290 } 291 292 // TODO: Rework this to be able to stop the gadget when an error occurs in 293 // run(). Notice it is the same for most of gadgets in the trace category. 294 go t.run() 295 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 296 297 return nil 298 } 299 300 func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) { 301 t.config.MountnsMap = mountnsMap 302 } 303 304 func (t *Tracer) SetEventHandler(handler any) { 305 nh, ok := handler.(func(ev *types.Event)) 306 if !ok { 307 panic("event handler invalid") 308 } 309 t.eventCallback = nh 310 } 311 312 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 313 tracer := &Tracer{ 314 config: &Config{}, 315 } 316 return tracer, nil 317 }