github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/snapshot/socket/tracer/tracer.go (about) 1 // Copyright 2021-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 "fmt" 21 "io" 22 "unsafe" 23 24 "github.com/cilium/ebpf" 25 "github.com/cilium/ebpf/link" 26 27 containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" 28 containerutils "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils" 29 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 30 socketcollectortypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/snapshot/socket/types" 31 "github.com/inspektor-gadget/inspektor-gadget/pkg/netnsenter" 32 eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 33 ) 34 35 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang -cflags ${CFLAGS} -type entry socket ./bpf/socket.bpf.c -- -Werror -O2 -g -c -x c 36 37 type Tracer struct { 38 iters []*link.Iter 39 40 // visitedNamespaces is a map where the key is the netns inode number and 41 // the value is the pid of one of the containers that share that netns. Such 42 // pid is used by NetnsEnter. TODO: Improve NetnsEnter to also work with the 43 // netns directly. 44 visitedNamespaces map[uint64]uint32 45 protocols socketcollectortypes.Proto 46 eventHandler func([]*socketcollectortypes.Event) 47 } 48 49 // Format from socket_bpf_seq_print() in bpf/socket_common.h 50 func parseStatus(proto string, statusUint uint8) (string, error) { 51 statusMap := [...]string{ 52 "ESTABLISHED", "SYN_SENT", "SYN_RECV", 53 "FIN_WAIT1", "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT", 54 "LAST_ACK", "LISTEN", "CLOSING", "NEW_SYN_RECV", 55 } 56 57 // Kernel enum starts from 1, adjust it to the statusMap 58 if statusUint == 0 || len(statusMap) <= int(statusUint-1) { 59 return "", fmt.Errorf("invalid %s status: %d", proto, statusUint) 60 } 61 status := statusMap[statusUint-1] 62 63 // Transform TCP status into something more suitable for UDP 64 if proto == "UDP" { 65 switch status { 66 case "ESTABLISHED": 67 status = "ACTIVE" 68 case "CLOSE": 69 status = "INACTIVE" 70 default: 71 return "", fmt.Errorf("unexpected %s status %s", proto, status) 72 } 73 } 74 75 return status, nil 76 } 77 78 func (t *Tracer) runCollector(pid uint32, netns uint64) ([]*socketcollectortypes.Event, error) { 79 sockets := []*socketcollectortypes.Event{} 80 err := netnsenter.NetnsEnter(int(pid), func() error { 81 for _, it := range t.iters { 82 reader, err := it.Open() 83 if err != nil { 84 return fmt.Errorf("opening BPF iterator: %w", err) 85 } 86 defer reader.Close() 87 88 buf, err := io.ReadAll(reader) 89 if err != nil { 90 return fmt.Errorf("reading BPF iterator: %w", err) 91 } 92 entrySize := int(unsafe.Sizeof(socketEntry{})) 93 94 for i := 0; i < len(buf)/entrySize; i++ { 95 entry := (*socketEntry)(unsafe.Pointer(&buf[i*entrySize])) 96 97 proto := gadgets.ProtoString(int(entry.Proto)) 98 status, err := parseStatus(proto, entry.State) 99 if err != nil { 100 return err 101 } 102 103 ipversion := gadgets.IPVerFromAF(entry.Family) 104 105 event := &socketcollectortypes.Event{ 106 Event: eventtypes.Event{ 107 Type: eventtypes.NORMAL, 108 }, 109 Protocol: proto, 110 SrcEndpoint: eventtypes.L4Endpoint{ 111 L3Endpoint: eventtypes.L3Endpoint{ 112 Addr: gadgets.IPStringFromBytes(entry.Saddr, ipversion), 113 Version: uint8(ipversion), 114 }, 115 Port: entry.Sport, 116 }, 117 DstEndpoint: eventtypes.L4Endpoint{ 118 L3Endpoint: eventtypes.L3Endpoint{ 119 Addr: gadgets.IPStringFromBytes(entry.Daddr, ipversion), 120 Version: uint8(ipversion), 121 }, 122 Port: entry.Dport, 123 }, 124 Status: status, 125 InodeNumber: entry.Inode, 126 WithNetNsID: eventtypes.WithNetNsID{NetNsID: netns}, 127 } 128 129 sockets = append(sockets, event) 130 } 131 } 132 return nil 133 }) 134 if err != nil { 135 return nil, err 136 } 137 138 return sockets, nil 139 } 140 141 // RunCollector is currently exported so it can be called from Collect(). It can be removed once 142 // pkg/gadget-collection/gadgets/snapshot/socket/gadget.go is gone. 143 func (t *Tracer) RunCollector(pid uint32, podname, namespace, node string) ([]*socketcollectortypes.Event, error) { 144 netns, err := containerutils.GetNetNs(int(pid)) 145 if err != nil { 146 return nil, fmt.Errorf("getting netns for pid %d: %w", pid, err) 147 } 148 149 sockets, err := t.runCollector(pid, netns) 150 if err != nil { 151 return nil, err 152 } 153 154 for _, socket := range sockets { 155 socket.K8s.Node = node 156 socket.K8s.Namespace = namespace 157 socket.K8s.PodName = podname 158 } 159 160 return sockets, nil 161 } 162 163 // --- 164 165 func NewTracer(protocols socketcollectortypes.Proto) (*Tracer, error) { 166 tracer := &Tracer{ 167 visitedNamespaces: make(map[uint64]uint32), 168 protocols: protocols, 169 } 170 171 if err := tracer.openIters(); err != nil { 172 tracer.CloseIters() 173 return nil, fmt.Errorf("installing tracer: %w", err) 174 } 175 176 return tracer, nil 177 } 178 179 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 180 return &Tracer{ 181 visitedNamespaces: make(map[uint64]uint32), 182 }, nil 183 } 184 185 func (t *Tracer) AttachContainer(container *containercollection.Container) error { 186 if _, ok := t.visitedNamespaces[container.Netns]; ok { 187 return nil 188 } 189 t.visitedNamespaces[container.Netns] = container.Pid 190 return nil 191 } 192 193 func (t *Tracer) DetachContainer(container *containercollection.Container) error { 194 return nil 195 } 196 197 func (t *Tracer) SetEventHandlerArray(handler any) { 198 nh, ok := handler.(func(ev []*socketcollectortypes.Event)) 199 if !ok { 200 panic("event handler invalid") 201 } 202 t.eventHandler = nh 203 } 204 205 // CloseIters is currently exported so it can be called from Collect() 206 func (t *Tracer) CloseIters() { 207 for _, it := range t.iters { 208 it.Close() 209 } 210 t.iters = nil 211 } 212 213 func (t *Tracer) openIters() error { 214 // TODO: how to avoid loading programs that aren't needed? 215 objs := &socketObjects{} 216 if err := loadSocketObjects(objs, nil); err != nil { 217 return err 218 } 219 220 toAttach := []*ebpf.Program{} 221 222 switch t.protocols { 223 case socketcollectortypes.TCP: 224 toAttach = append(toAttach, objs.IgSnapTcp) 225 case socketcollectortypes.UDP: 226 toAttach = append(toAttach, objs.IgSnapUdp) 227 case socketcollectortypes.ALL: 228 toAttach = append(toAttach, objs.IgSnapTcp, objs.IgSnapUdp) 229 } 230 231 for _, prog := range toAttach { 232 it, err := link.AttachIter(link.IterOptions{ 233 Program: prog, 234 }) 235 if err != nil { 236 var name string 237 if info, err := prog.Info(); err == nil { 238 name = info.Name 239 } 240 return fmt.Errorf("attaching program %q: %w", name, err) 241 } 242 t.iters = append(t.iters, it) 243 } 244 245 return nil 246 } 247 248 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 249 protocols := gadgetCtx.GadgetParams().Get(ParamProto).AsString() 250 t.protocols = socketcollectortypes.ProtocolsMap[protocols] 251 252 defer t.CloseIters() 253 if err := t.openIters(); err != nil { 254 return fmt.Errorf("installing tracer: %w", err) 255 } 256 257 allSockets := []*socketcollectortypes.Event{} 258 for netns, pid := range t.visitedNamespaces { 259 sockets, err := t.runCollector(pid, netns) 260 if err != nil { 261 return fmt.Errorf("snapshotting sockets in netns %d: %w", netns, err) 262 } 263 allSockets = append(allSockets, sockets...) 264 } 265 266 t.eventHandler(allSockets) 267 return nil 268 }