1
1
package features
2
2
3
3
import (
4
- "bytes"
5
4
"errors"
6
- "fmt"
7
5
"os"
8
6
"sync"
9
7
@@ -14,81 +12,6 @@ import (
14
12
"github.com/cilium/ebpf/internal/unix"
15
13
)
16
14
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
-
92
15
// HaveProgType probes the running kernel for the availability of the specified program type.
93
16
//
94
17
// Deprecated: use HaveProgramType() instead.
@@ -101,21 +24,6 @@ func HaveProgramType(pt ebpf.ProgramType) (err error) {
101
24
return haveProgramTypeMatrix .Result (pt )
102
25
}
103
26
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
-
119
27
func probeProgram (spec * ebpf.ProgramSpec ) error {
120
28
if spec .Instructions == nil {
121
29
spec .Instructions = asm.Instructions {
@@ -231,6 +139,18 @@ func init() {
231
139
}
232
140
}
233
141
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
+
234
154
// HaveProgramHelper probes the running kernel for the availability of the specified helper
235
155
// function to a specified program type.
236
156
// Return values have the following semantics:
@@ -250,42 +170,49 @@ func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) (err error)
250
170
err = wrapProbeErrors (err )
251
171
}()
252
172
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 {
265
173
if helper > helper .Max () {
266
174
return os .ErrInvalid
267
175
}
268
176
269
- return nil
177
+ return haveProgramHelper ( pt , helper )
270
178
}
271
179
272
180
func 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 {
276
186
return err
277
187
}
278
188
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
282
191
}
283
192
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" ,
287
201
}
288
202
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
+
289
216
switch {
290
217
// EACCES occurs when attempting to create a program probe with a helper
291
218
// 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 {
296
223
err = nil
297
224
298
225
// 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 ):
303
227
// TODO: possibly we need to check verifier output here to be sure
304
228
err = ebpf .ErrNotSupported
305
229
}
306
230
307
- pc.helpers [pt ][helper ] = err
308
-
231
+ helperCache .results [key ] = err
309
232
return err
310
233
}
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