github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/features/prog.go (about) 1 package features 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 8 "github.com/cilium/ebpf" 9 "github.com/cilium/ebpf/asm" 10 "github.com/cilium/ebpf/btf" 11 "github.com/cilium/ebpf/internal" 12 "github.com/cilium/ebpf/internal/sys" 13 "github.com/cilium/ebpf/internal/unix" 14 ) 15 16 // HaveProgType probes the running kernel for the availability of the specified program type. 17 // 18 // Deprecated: use HaveProgramType() instead. 19 var HaveProgType = HaveProgramType 20 21 // HaveProgramType probes the running kernel for the availability of the specified program type. 22 // 23 // See the package documentation for the meaning of the error return value. 24 func HaveProgramType(pt ebpf.ProgramType) (err error) { 25 return haveProgramTypeMatrix.Result(pt) 26 } 27 28 func probeProgram(spec *ebpf.ProgramSpec) error { 29 if spec.Instructions == nil { 30 spec.Instructions = asm.Instructions{ 31 asm.LoadImm(asm.R0, 0, asm.DWord), 32 asm.Return(), 33 } 34 } 35 prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{ 36 LogDisabled: true, 37 }) 38 if err == nil { 39 prog.Close() 40 } 41 42 switch { 43 // EINVAL occurs when attempting to create a program with an unknown type. 44 // E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end 45 // of the struct known by the running kernel, meaning the kernel is too old 46 // to support the given prog type. 47 case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG): 48 err = ebpf.ErrNotSupported 49 } 50 51 return err 52 } 53 54 var haveProgramTypeMatrix = internal.FeatureMatrix[ebpf.ProgramType]{ 55 ebpf.SocketFilter: {Version: "3.19"}, 56 ebpf.Kprobe: {Version: "4.1"}, 57 ebpf.SchedCLS: {Version: "4.1"}, 58 ebpf.SchedACT: {Version: "4.1"}, 59 ebpf.TracePoint: {Version: "4.7"}, 60 ebpf.XDP: {Version: "4.8"}, 61 ebpf.PerfEvent: {Version: "4.9"}, 62 ebpf.CGroupSKB: {Version: "4.10"}, 63 ebpf.CGroupSock: {Version: "4.10"}, 64 ebpf.LWTIn: {Version: "4.10"}, 65 ebpf.LWTOut: {Version: "4.10"}, 66 ebpf.LWTXmit: {Version: "4.10"}, 67 ebpf.SockOps: {Version: "4.13"}, 68 ebpf.SkSKB: {Version: "4.14"}, 69 ebpf.CGroupDevice: {Version: "4.15"}, 70 ebpf.SkMsg: {Version: "4.17"}, 71 ebpf.RawTracepoint: {Version: "4.17"}, 72 ebpf.CGroupSockAddr: { 73 Version: "4.17", 74 Fn: func() error { 75 return probeProgram(&ebpf.ProgramSpec{ 76 Type: ebpf.CGroupSockAddr, 77 AttachType: ebpf.AttachCGroupInet4Connect, 78 }) 79 }, 80 }, 81 ebpf.LWTSeg6Local: {Version: "4.18"}, 82 ebpf.LircMode2: {Version: "4.18"}, 83 ebpf.SkReuseport: {Version: "4.19"}, 84 ebpf.FlowDissector: {Version: "4.20"}, 85 ebpf.CGroupSysctl: {Version: "5.2"}, 86 ebpf.RawTracepointWritable: {Version: "5.2"}, 87 ebpf.CGroupSockopt: { 88 Version: "5.3", 89 Fn: func() error { 90 return probeProgram(&ebpf.ProgramSpec{ 91 Type: ebpf.CGroupSockopt, 92 AttachType: ebpf.AttachCGroupGetsockopt, 93 }) 94 }, 95 }, 96 ebpf.Tracing: { 97 Version: "5.5", 98 Fn: func() error { 99 return probeProgram(&ebpf.ProgramSpec{ 100 Type: ebpf.Tracing, 101 AttachType: ebpf.AttachTraceFEntry, 102 AttachTo: "bpf_init", 103 }) 104 }, 105 }, 106 ebpf.StructOps: { 107 Version: "5.6", 108 Fn: func() error { 109 err := probeProgram(&ebpf.ProgramSpec{ 110 Type: ebpf.StructOps, 111 License: "GPL", 112 }) 113 if errors.Is(err, sys.ENOTSUPP) { 114 // ENOTSUPP means the program type is at least known to the kernel. 115 return nil 116 } 117 return err 118 }, 119 }, 120 ebpf.Extension: { 121 Version: "5.6", 122 Fn: func() error { 123 // create btf.Func to add to first ins of target and extension so both progs are btf powered 124 btfFn := btf.Func{ 125 Name: "a", 126 Type: &btf.FuncProto{ 127 Return: &btf.Int{}, 128 Params: []btf.FuncParam{ 129 {Name: "ctx", Type: &btf.Pointer{Target: &btf.Struct{Name: "xdp_md"}}}, 130 }, 131 }, 132 Linkage: btf.GlobalFunc, 133 } 134 insns := asm.Instructions{ 135 btf.WithFuncMetadata(asm.Mov.Imm(asm.R0, 0), &btfFn), 136 asm.Return(), 137 } 138 139 // create target prog 140 prog, err := ebpf.NewProgramWithOptions( 141 &ebpf.ProgramSpec{ 142 Type: ebpf.XDP, 143 Instructions: insns, 144 }, 145 ebpf.ProgramOptions{ 146 LogDisabled: true, 147 }, 148 ) 149 if err != nil { 150 return err 151 } 152 defer prog.Close() 153 154 // probe for Extension prog with target 155 return probeProgram(&ebpf.ProgramSpec{ 156 Type: ebpf.Extension, 157 Instructions: insns, 158 AttachTarget: prog, 159 AttachTo: btfFn.Name, 160 }) 161 }, 162 }, 163 ebpf.LSM: { 164 Version: "5.7", 165 Fn: func() error { 166 return probeProgram(&ebpf.ProgramSpec{ 167 Type: ebpf.LSM, 168 AttachType: ebpf.AttachLSMMac, 169 AttachTo: "file_mprotect", 170 License: "GPL", 171 }) 172 }, 173 }, 174 ebpf.SkLookup: { 175 Version: "5.9", 176 Fn: func() error { 177 return probeProgram(&ebpf.ProgramSpec{ 178 Type: ebpf.SkLookup, 179 AttachType: ebpf.AttachSkLookup, 180 }) 181 }, 182 }, 183 ebpf.Syscall: { 184 Version: "5.14", 185 Fn: func() error { 186 return probeProgram(&ebpf.ProgramSpec{ 187 Type: ebpf.Syscall, 188 Flags: unix.BPF_F_SLEEPABLE, 189 }) 190 }, 191 }, 192 } 193 194 func init() { 195 for key, ft := range haveProgramTypeMatrix { 196 ft.Name = key.String() 197 if ft.Fn == nil { 198 key := key // avoid the dreaded loop variable problem 199 ft.Fn = func() error { return probeProgram(&ebpf.ProgramSpec{Type: key}) } 200 } 201 } 202 } 203 204 type helperKey struct { 205 typ ebpf.ProgramType 206 helper asm.BuiltinFunc 207 } 208 209 var helperCache = internal.NewFeatureCache(func(key helperKey) *internal.FeatureTest { 210 return &internal.FeatureTest{ 211 Name: fmt.Sprintf("%s for program type %s", key.helper, key.typ), 212 Fn: func() error { 213 return haveProgramHelper(key.typ, key.helper) 214 }, 215 } 216 }) 217 218 // HaveProgramHelper probes the running kernel for the availability of the specified helper 219 // function to a specified program type. 220 // Return values have the following semantics: 221 // 222 // err == nil: The feature is available. 223 // errors.Is(err, ebpf.ErrNotSupported): The feature is not available. 224 // err != nil: Any errors encountered during probe execution, wrapped. 225 // 226 // Note that the latter case may include false negatives, and that program creation may 227 // succeed despite an error being returned. 228 // Only `nil` and `ebpf.ErrNotSupported` are conclusive. 229 // 230 // Probe results are cached and persist throughout any process capability changes. 231 func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { 232 if helper > helper.Max() { 233 return os.ErrInvalid 234 } 235 236 return helperCache.Result(helperKey{pt, helper}) 237 } 238 239 func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { 240 if ok := helperProbeNotImplemented(pt); ok { 241 return fmt.Errorf("no feature probe for %v/%v", pt, helper) 242 } 243 244 if err := HaveProgramType(pt); err != nil { 245 return err 246 } 247 248 spec := &ebpf.ProgramSpec{ 249 Type: pt, 250 Instructions: asm.Instructions{ 251 helper.Call(), 252 asm.LoadImm(asm.R0, 0, asm.DWord), 253 asm.Return(), 254 }, 255 License: "GPL", 256 } 257 258 switch pt { 259 case ebpf.CGroupSockAddr: 260 spec.AttachType = ebpf.AttachCGroupInet4Connect 261 case ebpf.CGroupSockopt: 262 spec.AttachType = ebpf.AttachCGroupGetsockopt 263 case ebpf.SkLookup: 264 spec.AttachType = ebpf.AttachSkLookup 265 case ebpf.Syscall: 266 spec.Flags = unix.BPF_F_SLEEPABLE 267 } 268 269 prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{ 270 LogDisabled: true, 271 }) 272 if err == nil { 273 prog.Close() 274 } 275 276 switch { 277 // EACCES occurs when attempting to create a program probe with a helper 278 // while the register args when calling this helper aren't set up properly. 279 // We interpret this as the helper being available, because the verifier 280 // returns EINVAL if the helper is not supported by the running kernel. 281 case errors.Is(err, unix.EACCES): 282 // TODO: possibly we need to check verifier output here to be sure 283 err = nil 284 285 // EINVAL occurs when attempting to create a program with an unknown helper. 286 case errors.Is(err, unix.EINVAL): 287 // TODO: possibly we need to check verifier output here to be sure 288 err = ebpf.ErrNotSupported 289 } 290 291 return err 292 } 293 294 func helperProbeNotImplemented(pt ebpf.ProgramType) bool { 295 switch pt { 296 case ebpf.Extension, ebpf.LSM, ebpf.StructOps, ebpf.Tracing: 297 return true 298 } 299 return false 300 }