Skip to content

Commit cc8154c

Browse files
committed
features: simplify HaveProgramHelper
1 parent e3a8cab commit cc8154c

File tree

4 files changed

+48
-195
lines changed

4 files changed

+48
-195
lines changed

features/prog.go

+43-128
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package features
22

33
import (
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-
11927
func 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

272180
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 {
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-
}

features/prog_test.go

+3-58
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,6 @@ import (
1212
"github.com/cilium/ebpf/internal/testutils"
1313
)
1414

15-
var progTypeMinVersion = map[ebpf.ProgramType]string{
16-
ebpf.SocketFilter: "3.19",
17-
ebpf.Kprobe: "4.1",
18-
ebpf.SchedCLS: "4.1",
19-
ebpf.SchedACT: "4.1",
20-
ebpf.TracePoint: "4.7",
21-
ebpf.XDP: "4.8",
22-
ebpf.PerfEvent: "4.9",
23-
ebpf.CGroupSKB: "4.10",
24-
ebpf.CGroupSock: "4.10",
25-
ebpf.LWTIn: "4.10",
26-
ebpf.LWTOut: "4.10",
27-
ebpf.LWTXmit: "4.10",
28-
ebpf.SockOps: "4.13",
29-
ebpf.SkSKB: "4.14",
30-
ebpf.CGroupDevice: "4.15",
31-
ebpf.SkMsg: "4.17",
32-
ebpf.RawTracepoint: "4.17",
33-
ebpf.CGroupSockAddr: "4.17",
34-
ebpf.LWTSeg6Local: "4.18",
35-
ebpf.LircMode2: "4.18",
36-
ebpf.SkReuseport: "4.19",
37-
ebpf.FlowDissector: "4.20",
38-
ebpf.CGroupSysctl: "5.2",
39-
ebpf.RawTracepointWritable: "5.2",
40-
ebpf.CGroupSockopt: "5.3",
41-
ebpf.Tracing: "5.5",
42-
ebpf.StructOps: "5.6",
43-
ebpf.Extension: "5.6",
44-
ebpf.LSM: "5.7",
45-
ebpf.SkLookup: "5.9",
46-
ebpf.Syscall: "5.14",
47-
}
48-
4915
func TestHaveProgramType(t *testing.T) {
5016
testutils.CheckFeatureMatrix(t, haveProgramTypeMatrix)
5117
}
@@ -97,26 +63,10 @@ func TestHaveProgramHelper(t *testing.T) {
9763
}
9864

9965
for _, tc := range testCases {
100-
minVersion := progTypeMinVersion[tc.prog]
101-
102-
progVersion, err := internal.NewVersion(minVersion)
103-
if err != nil {
104-
t.Fatalf("Could not read kernel version required for program: %v", err)
105-
}
106-
107-
helperVersion, err := internal.NewVersion(tc.version)
108-
if err != nil {
109-
t.Fatalf("Could not read kernel version required for helper: %v", err)
110-
}
111-
112-
if progVersion.Less(helperVersion) {
113-
minVersion = tc.version
114-
}
115-
11666
t.Run(fmt.Sprintf("%s/%s", tc.prog.String(), tc.helper.String()), func(t *testing.T) {
11767
feature := fmt.Sprintf("helper %s for program type %s", tc.helper.String(), tc.prog.String())
11868

119-
testutils.SkipOnOldKernel(t, minVersion, feature)
69+
testutils.SkipOnOldKernel(t, tc.version, feature)
12070

12171
err := HaveProgramHelper(tc.prog, tc.helper)
12272
if !errors.Is(err, tc.expected) {
@@ -129,14 +79,9 @@ func TestHaveProgramHelper(t *testing.T) {
12979
}
13080

13181
func TestHaveProgramHelperUnsupported(t *testing.T) {
132-
pt := ebpf.SocketFilter
133-
minVersion := progTypeMinVersion[pt]
134-
135-
feature := fmt.Sprintf("program type %s", pt.String())
136-
137-
testutils.SkipOnOldKernel(t, minVersion, feature)
82+
testutils.SkipIfNotSupported(t, HaveProgramType(ebpf.SocketFilter))
13883

139-
if err := haveProgramHelper(pt, asm.BuiltinFunc(math.MaxInt32)); !errors.Is(err, ebpf.ErrNotSupported) {
84+
if err := haveProgramHelper(ebpf.SocketFilter, asm.BuiltinFunc(math.MaxInt32)); !errors.Is(err, ebpf.ErrNotSupported) {
14085
t.Fatalf("Expected ebpf.ErrNotSupported but was: %v", err)
14186
}
14287
}

types.go

-6
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,6 @@ func (mt MapType) canStoreProgram() bool {
117117
// ProgramType of the eBPF program
118118
type ProgramType uint32
119119

120-
// Max return the latest supported ProgramType.
121-
func (ProgramType) Max() ProgramType {
122-
return maxProgramType - 1
123-
}
124-
125120
// eBPF program types
126121
const (
127122
UnspecifiedProgram ProgramType = iota
@@ -156,7 +151,6 @@ const (
156151
LSM
157152
SkLookup
158153
Syscall
159-
maxProgramType
160154
)
161155

162156
// AttachType of the eBPF program, needed to differentiate allowed context accesses in

types_string.go

+2-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)