github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/helpers.go (about) 1 // Copyright 2019-2024 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 gadgets 18 19 import ( 20 "encoding/binary" 21 "fmt" 22 "net" 23 "net/netip" 24 "time" 25 "unsafe" 26 27 "github.com/cilium/ebpf" 28 "github.com/cilium/ebpf/asm" 29 "github.com/cilium/ebpf/features" 30 "github.com/cilium/ebpf/link" 31 "golang.org/x/sys/unix" 32 33 "github.com/inspektor-gadget/inspektor-gadget/pkg/btfgen" 34 "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 35 ) 36 37 // CloseLink closes l if it's not nil and returns nil 38 func CloseLink(l link.Link) link.Link { 39 if l != nil { 40 l.Close() 41 } 42 return nil 43 } 44 45 // DataEnricherByMntNs is used to enrich events with Kubernetes information, 46 // like node, namespace, pod name and container name when the mount namespace 47 // is available. 48 type DataEnricherByMntNs interface { 49 EnrichByMntNs(event *types.CommonData, mountnsid uint64) 50 } 51 52 // DataNodeEnricher is used to enrich events with Kubernetes node, without 53 // needing any namespace. 54 type DataNodeEnricher interface { 55 EnrichNode(event *types.CommonData) 56 } 57 58 // DataEnricherByNetNs is used to enrich events with Kubernetes information, 59 // like node, namespace, pod name and container name when the network namespace 60 // is available. 61 type DataEnricherByNetNs interface { 62 EnrichByNetNs(event *types.CommonData, netnsid uint64) 63 } 64 65 func Htonl(hl uint32) uint32 { 66 var nl [4]byte 67 binary.BigEndian.PutUint32(nl[:], hl) 68 return *(*uint32)(unsafe.Pointer(&nl[0])) 69 } 70 71 func Htons(hs uint16) uint16 { 72 var ns [2]byte 73 binary.BigEndian.PutUint16(ns[:], hs) 74 return *(*uint16)(unsafe.Pointer(&ns[0])) 75 } 76 77 func IPStringFromBytes(ipBytes [16]byte, ipType int) string { 78 switch ipType { 79 case 4: 80 return netip.AddrFrom4(*(*[4]byte)(ipBytes[0:4])).String() 81 case 6: 82 return netip.AddrFrom16(ipBytes).String() 83 default: 84 return "" 85 } 86 } 87 88 // IPStringToByteArray converts an IP address (IPv6 only) string to a uint32 89 // in big-endian. 90 func IPStringToByteArray(ipAddr string) ([16]byte, error) { 91 addr, err := netip.ParseAddr(ipAddr) 92 if err != nil { 93 return [16]byte{}, fmt.Errorf("invalid IP address: %s", ipAddr) 94 } 95 96 if !addr.Is6() { 97 return [16]byte{}, fmt.Errorf("IP address is not IPv6: %s", addr) 98 } 99 100 // This function ensures us the order is big endian: 101 // https://cs.opensource.google/go/go/+/refs/tags/go1.20.5:src/net/netip/netip.go;drc=dc98ccd836da7d22a5d270b9778fb055826fa07b;l=676 102 return addr.As16(), nil 103 } 104 105 // IPStringToUint32 converts an IP address (IPv4 only) string to a uint32 106 // in big-endian. 107 func IPStringToUint32(ipAddr string) (uint32, error) { 108 // Notice ipAddr is already expressed in big-endian and net.ParseIP stores 109 // it in a byte array in big-endian too. 110 ip := net.ParseIP(ipAddr).To4() 111 if ip == nil { 112 return 0, fmt.Errorf("invalid IP address: %s", ipAddr) 113 } 114 // Convert the byte array to a uint32 keeping the big-endian order. We don't 115 // use binary.[BigEndian|LittleEndian].Uint32() to make this code portable. 116 return *(*uint32)(unsafe.Pointer(&ip[0])), nil 117 } 118 119 func IPVerFromAF(af uint16) int { 120 switch af { 121 case unix.AF_INET: 122 return 4 123 case unix.AF_INET6: 124 return 6 125 default: 126 return 0 127 } 128 } 129 130 var timeDiff time.Duration 131 132 func init() { 133 var t unix.Timespec 134 err := unix.ClockGettime(unix.CLOCK_BOOTTIME, &t) 135 if err != nil { 136 panic(err) 137 } 138 timeDiff = time.Duration(time.Now().UnixNano() - t.Sec*1000*1000*1000 - t.Nsec) 139 } 140 141 // WallTimeFromBootTime converts a time from bpf_ktime_get_boot_ns() to the 142 // wall time with nano precision. 143 // 144 // Example: 145 // 146 // fmt.Printf("Time: %s\n", WallTimeFromBootTime(ts).String()) 147 // 148 // would display: 149 // 150 // Time: 2022-12-15T16:49:00.452371948+01:00 151 // 152 // Shell command to convert the number to a date: 153 // 154 // $ date -d @$(echo 1671447636499110634/1000000000|bc -l) +"%d-%m-%Y %H:%M:%S:%N" 155 // 19-12-2022 12:00:36:499110634 156 // 157 // bpf_ktime_get_boot_ns was added in Linux 5.7. If not available and the BPF 158 // program returns 0, just get the timestamp in userspace. 159 func WallTimeFromBootTime(ts uint64) types.Time { 160 if ts == 0 { 161 return types.Time(time.Now().UnixNano()) 162 } 163 return types.Time(time.Unix(0, int64(ts)).Add(timeDiff).UnixNano()) 164 } 165 166 // HasBpfKtimeGetBootNs returns true if bpf_ktime_get_boot_ns is available 167 func HasBpfKtimeGetBootNs() bool { 168 // We only care about the helper, hence test with ebpf.SocketFilter that exist in all 169 // kernels that support ebpf. 170 err := features.HaveProgramHelper(ebpf.SocketFilter, asm.FnKtimeGetBootNs) 171 return err == nil 172 } 173 174 // removeBpfKtimeGetBootNs removes calls to bpf_ktime_get_boot_ns and replaces 175 // it by an assignment to zero 176 func removeBpfKtimeGetBootNs(p *ebpf.ProgramSpec) { 177 iter := p.Instructions.Iterate() 178 179 for iter.Next() { 180 in := iter.Ins 181 182 if in.OpCode.Class().IsJump() && 183 in.OpCode.JumpOp() == asm.Call && 184 in.Constant == int64(asm.FnKtimeGetBootNs) { 185 // reset timestamp to zero 186 in.OpCode = asm.Mov.Op(asm.ImmSource) 187 in.Dst = asm.R0 188 in.Constant = 0 189 } 190 } 191 } 192 193 // FixBpfKtimeGetBootNs checks if bpf_ktime_get_boot_ns is supported by the 194 // kernel and removes it if not 195 func FixBpfKtimeGetBootNs(programSpecs map[string]*ebpf.ProgramSpec) { 196 if HasBpfKtimeGetBootNs() { 197 return 198 } 199 200 for _, s := range programSpecs { 201 removeBpfKtimeGetBootNs(s) 202 } 203 } 204 205 // LoadeBPFSpec is a helper to load an eBPF spec from gadgets. 206 // It replaces filter map and calls the necessary functions to load 207 // Maps and Programs into the kernel 208 func LoadeBPFSpec( 209 mountnsMap *ebpf.Map, 210 spec *ebpf.CollectionSpec, 211 consts map[string]interface{}, 212 objs interface{}, 213 ) error { 214 FixBpfKtimeGetBootNs(spec.Programs) 215 216 mapReplacements := map[string]*ebpf.Map{} 217 filterByMntNs := false 218 219 if mountnsMap != nil { 220 filterByMntNs = true 221 mapReplacements[MntNsFilterMapName] = mountnsMap 222 } 223 224 if consts == nil { 225 consts = map[string]interface{}{} 226 } 227 228 consts[FilterByMntNsName] = filterByMntNs 229 230 if err := spec.RewriteConstants(consts); err != nil { 231 return fmt.Errorf("rewriting constants: %w", err) 232 } 233 234 opts := ebpf.CollectionOptions{ 235 MapReplacements: mapReplacements, 236 Programs: ebpf.ProgramOptions{ 237 KernelTypes: btfgen.GetBTFSpec(), 238 }, 239 } 240 241 if err := spec.LoadAndAssign(objs, &opts); err != nil { 242 return fmt.Errorf("loading maps and programs: %w", err) 243 } 244 245 return nil 246 }