11package features
22
33import (
4- "bytes"
54 "errors"
6- "fmt"
75 "os"
86 "sync"
97
@@ -14,81 +12,6 @@ import (
1412 "github.com/cilium/ebpf/internal/unix"
1513)
1614
17- func init () {
18- pc .helpers = make (map [ebpf.ProgramType ]map [asm.BuiltinFunc ]error )
19- allocHelperCache ()
20- }
21-
22- func allocHelperCache () {
23- for pt := ebpf .UnspecifiedProgram + 1 ; pt <= pt .Max (); pt ++ {
24- pc .helpers [pt ] = make (map [asm.BuiltinFunc ]error )
25- }
26- }
27-
28- var (
29- pc progCache
30- )
31-
32- type progCache struct {
33- helperMu sync.Mutex
34- helpers map [ebpf.ProgramType ]map [asm.BuiltinFunc ]error
35- }
36-
37- func createProgLoadAttr (pt ebpf.ProgramType , helper asm.BuiltinFunc ) (* sys.ProgLoadAttr , error ) {
38- var expectedAttachType ebpf.AttachType
39- var progFlags uint32
40-
41- insns := asm.Instructions {
42- asm .LoadImm (asm .R0 , 0 , asm .DWord ),
43- asm .Return (),
44- }
45-
46- if helper != asm .FnUnspec {
47- insns = append (asm.Instructions {helper .Call ()}, insns ... )
48- }
49-
50- buf := bytes .NewBuffer (make ([]byte , 0 , insns .Size ()))
51- if err := insns .Marshal (buf , internal .NativeEndian ); err != nil {
52- return nil , err
53- }
54-
55- bytecode := buf .Bytes ()
56- instructions := sys .NewSlicePointer (bytecode )
57-
58- // Some programs have expected attach types which are checked during the
59- // BPF_PROG_LOAD syscall.
60- switch pt {
61- case ebpf .CGroupSockAddr :
62- expectedAttachType = ebpf .AttachCGroupInet4Connect
63- case ebpf .CGroupSockopt :
64- expectedAttachType = ebpf .AttachCGroupGetsockopt
65- case ebpf .SkLookup :
66- expectedAttachType = ebpf .AttachSkLookup
67- case ebpf .Syscall :
68- progFlags = unix .BPF_F_SLEEPABLE
69- default :
70- expectedAttachType = ebpf .AttachNone
71- }
72-
73- // Kernels before 5.0 (6c4fc209fcf9 "bpf: remove useless version check for prog load")
74- // require the version field to be set to the value of the KERNEL_VERSION
75- // macro for kprobe-type programs.
76- v , err := internal .KernelVersion ()
77- if err != nil {
78- return nil , fmt .Errorf ("detecting kernel version: %w" , err )
79- }
80-
81- return & sys.ProgLoadAttr {
82- ProgType : sys .ProgType (pt ),
83- Insns : instructions ,
84- InsnCnt : uint32 (len (bytecode ) / asm .InstructionSize ),
85- ProgFlags : progFlags ,
86- ExpectedAttachType : sys .AttachType (expectedAttachType ),
87- License : sys .NewStringPointer ("GPL" ),
88- KernVersion : v .Kernel (),
89- }, nil
90- }
91-
9215// HaveProgType probes the running kernel for the availability of the specified program type.
9316//
9417// Deprecated: use HaveProgramType() instead.
@@ -101,21 +24,6 @@ func HaveProgramType(pt ebpf.ProgramType) (err error) {
10124 return haveProgramTypeMatrix .Result (pt )
10225}
10326
104- func validateProgramType (pt ebpf.ProgramType ) error {
105- if pt > pt .Max () {
106- return os .ErrInvalid
107- }
108-
109- if progLoadProbeNotImplemented (pt ) {
110- // A probe for a these prog types has BTF requirements we currently cannot meet
111- // Once we figure out how to add a working probe in this package, we can remove
112- // this check
113- return fmt .Errorf ("a probe for ProgType %s isn't implemented" , pt .String ())
114- }
115-
116- return nil
117- }
118-
11927func probeProgram (spec * ebpf.ProgramSpec ) error {
12028 if spec .Instructions == nil {
12129 spec .Instructions = asm.Instructions {
@@ -231,6 +139,18 @@ func init() {
231139 }
232140}
233141
142+ type helperKey struct {
143+ typ ebpf.ProgramType
144+ helper asm.BuiltinFunc
145+ }
146+
147+ var helperCache = struct {
148+ sync.Mutex
149+ results map [helperKey ]error
150+ }{
151+ results : make (map [helperKey ]error ),
152+ }
153+
234154// HaveProgramHelper probes the running kernel for the availability of the specified helper
235155// function to a specified program type.
236156// Return values have the following semantics:
@@ -250,42 +170,49 @@ func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) (err error)
250170 err = wrapProbeErrors (err )
251171 }()
252172
253- if err := validateProgramType (pt ); err != nil {
254- return err
255- }
256-
257- if err := validateProgramHelper (helper ); err != nil {
258- return err
259- }
260-
261- return haveProgramHelper (pt , helper )
262- }
263-
264- func validateProgramHelper (helper asm.BuiltinFunc ) error {
265173 if helper > helper .Max () {
266174 return os .ErrInvalid
267175 }
268176
269- return nil
177+ return haveProgramHelper ( pt , helper )
270178}
271179
272180func haveProgramHelper (pt ebpf.ProgramType , helper asm.BuiltinFunc ) error {
273- pc .helperMu .Lock ()
274- defer pc .helperMu .Unlock ()
275- if err , ok := pc.helpers [pt ][helper ]; ok {
181+ helperCache .Lock ()
182+ defer helperCache .Unlock ()
183+
184+ key := helperKey {pt , helper }
185+ if err , ok := helperCache .results [key ]; ok {
276186 return err
277187 }
278188
279- attr , err := createProgLoadAttr (pt , helper )
280- if err != nil {
281- return fmt .Errorf ("couldn't create the program load attribute: %w" , err )
189+ if err := HaveProgramType (pt ); err != nil {
190+ return err
282191 }
283192
284- fd , err := sys .ProgLoad (attr )
285- if fd != nil {
286- fd .Close ()
193+ spec := & ebpf.ProgramSpec {
194+ Type : pt ,
195+ Instructions : asm.Instructions {
196+ helper .Call (),
197+ asm .LoadImm (asm .R0 , 0 , asm .DWord ),
198+ asm .Return (),
199+ },
200+ License : "GPL" ,
287201 }
288202
203+ switch pt {
204+ case ebpf .CGroupSockAddr :
205+ spec .AttachType = ebpf .AttachCGroupInet4Connect
206+ case ebpf .CGroupSockopt :
207+ spec .AttachType = ebpf .AttachCGroupGetsockopt
208+ case ebpf .SkLookup :
209+ spec .AttachType = ebpf .AttachSkLookup
210+ case ebpf .Syscall :
211+ spec .Flags = unix .BPF_F_SLEEPABLE
212+ }
213+
214+ err := probeProgram (spec )
215+
289216 switch {
290217 // EACCES occurs when attempting to create a program probe with a helper
291218 // while the register args when calling this helper aren't set up properly.
@@ -296,23 +223,11 @@ func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {
296223 err = nil
297224
298225 // EINVAL occurs when attempting to create a program with an unknown helper.
299- // E2BIG occurs when BPFProgLoadAttr contains non-zero bytes past the end
300- // of the struct known by the running kernel, meaning the kernel is too old
301- // to support the given prog type.
302- case errors .Is (err , unix .EINVAL ), errors .Is (err , unix .E2BIG ):
226+ case errors .Is (err , unix .EINVAL ):
303227 // TODO: possibly we need to check verifier output here to be sure
304228 err = ebpf .ErrNotSupported
305229 }
306230
307- pc.helpers [pt ][helper ] = err
308-
231+ helperCache .results [key ] = err
309232 return err
310233}
311-
312- func progLoadProbeNotImplemented (pt ebpf.ProgramType ) bool {
313- switch pt {
314- case ebpf .Tracing , ebpf .Extension , ebpf .LSM :
315- return true
316- }
317- return false
318- }
0 commit comments