github.com/cilium/ebpf@v0.15.0/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 }, 129 Linkage: btf.GlobalFunc, 130 } 131 insns := asm.Instructions{ 132 btf.WithFuncMetadata(asm.Mov.Imm(asm.R0, 0), &btfFn), 133 asm.Return(), 134 } 135 136 // create target prog 137 prog, err := ebpf.NewProgramWithOptions( 138 &ebpf.ProgramSpec{ 139 Type: ebpf.XDP, 140 Instructions: insns, 141 }, 142 ebpf.ProgramOptions{ 143 LogDisabled: true, 144 }, 145 ) 146 if err != nil { 147 return err 148 } 149 defer prog.Close() 150 151 // probe for Extension prog with target 152 return probeProgram(&ebpf.ProgramSpec{ 153 Type: ebpf.Extension, 154 Instructions: insns, 155 AttachTarget: prog, 156 AttachTo: btfFn.Name, 157 }) 158 }, 159 }, 160 ebpf.LSM: { 161 Version: "5.7", 162 Fn: func() error { 163 return probeProgram(&ebpf.ProgramSpec{ 164 Type: ebpf.LSM, 165 AttachType: ebpf.AttachLSMMac, 166 AttachTo: "file_mprotect", 167 License: "GPL", 168 }) 169 }, 170 }, 171 ebpf.SkLookup: { 172 Version: "5.9", 173 Fn: func() error { 174 return probeProgram(&ebpf.ProgramSpec{ 175 Type: ebpf.SkLookup, 176 AttachType: ebpf.AttachSkLookup, 177 }) 178 }, 179 }, 180 ebpf.Syscall: { 181 Version: "5.14", 182 Fn: func() error { 183 return probeProgram(&ebpf.ProgramSpec{ 184 Type: ebpf.Syscall, 185 Flags: unix.BPF_F_SLEEPABLE, 186 }) 187 }, 188 }, 189 } 190 191 func init() { 192 for key, ft := range haveProgramTypeMatrix { 193 ft.Name = key.String() 194 if ft.Fn == nil { 195 key := key // avoid the dreaded loop variable problem 196 ft.Fn = func() error { return probeProgram(&ebpf.ProgramSpec{Type: key}) } 197 } 198 } 199 } 200 201 type helperKey struct { 202 typ ebpf.ProgramType 203 helper asm.BuiltinFunc 204 } 205 206 var helperCache = internal.NewFeatureCache(func(key helperKey) *internal.FeatureTest { 207 return &internal.FeatureTest{ 208 Name: fmt.Sprintf("%s for program type %s", key.helper, key.typ), 209 Fn: func() error { 210 return haveProgramHelper(key.typ, key.helper) 211 }, 212 } 213 }) 214 215 // HaveProgramHelper probes the running kernel for the availability of the specified helper 216 // function to a specified program type. 217 // Return values have the following semantics: 218 // 219 // err == nil: The feature is available. 220 // errors.Is(err, ebpf.ErrNotSupported): The feature is not available. 221 // err != nil: Any errors encountered during probe execution, wrapped. 222 // 223 // Note that the latter case may include false negatives, and that program creation may 224 // succeed despite an error being returned. 225 // Only `nil` and `ebpf.ErrNotSupported` are conclusive. 226 // 227 // Probe results are cached and persist throughout any process capability changes. 228 func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { 229 if helper > helper.Max() { 230 return os.ErrInvalid 231 } 232 233 return helperCache.Result(helperKey{pt, helper}) 234 } 235 236 func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { 237 if ok := helperProbeNotImplemented(pt); ok { 238 return fmt.Errorf("no feature probe for %v/%v", pt, helper) 239 } 240 241 if err := HaveProgramType(pt); err != nil { 242 return err 243 } 244 245 spec := &ebpf.ProgramSpec{ 246 Type: pt, 247 Instructions: asm.Instructions{ 248 helper.Call(), 249 asm.LoadImm(asm.R0, 0, asm.DWord), 250 asm.Return(), 251 }, 252 License: "GPL", 253 } 254 255 switch pt { 256 case ebpf.CGroupSockAddr: 257 spec.AttachType = ebpf.AttachCGroupInet4Connect 258 case ebpf.CGroupSockopt: 259 spec.AttachType = ebpf.AttachCGroupGetsockopt 260 case ebpf.SkLookup: 261 spec.AttachType = ebpf.AttachSkLookup 262 case ebpf.Syscall: 263 spec.Flags = unix.BPF_F_SLEEPABLE 264 } 265 266 prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{ 267 LogDisabled: true, 268 }) 269 if err == nil { 270 prog.Close() 271 } 272 273 switch { 274 // EACCES occurs when attempting to create a program probe with a helper 275 // while the register args when calling this helper aren't set up properly. 276 // We interpret this as the helper being available, because the verifier 277 // returns EINVAL if the helper is not supported by the running kernel. 278 case errors.Is(err, unix.EACCES): 279 // TODO: possibly we need to check verifier output here to be sure 280 err = nil 281 282 // EINVAL occurs when attempting to create a program with an unknown helper. 283 case errors.Is(err, unix.EINVAL): 284 // TODO: possibly we need to check verifier output here to be sure 285 err = ebpf.ErrNotSupported 286 } 287 288 return err 289 } 290 291 func helperProbeNotImplemented(pt ebpf.ProgramType) bool { 292 switch pt { 293 case ebpf.Extension, ebpf.LSM, ebpf.StructOps, ebpf.Tracing: 294 return true 295 } 296 return false 297 }