Skip to content

Commit 1cb2253

Browse files
committed
token: feature detection
Signed-off-by: Andrej Svenke <anryko@nebius.com>
1 parent ac41438 commit 1cb2253

33 files changed

+696
-345
lines changed

bpffs.go renamed to bpffs/bpffs.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package ebpf
1+
package bpffs
22

33
import (
44
"errors"
@@ -58,7 +58,7 @@ func (bf *BPFFS) Close() error {
5858
return errors.Join(errs...)
5959
}
6060

61-
func (bf *BPFFS) token() (*sys.FD, error) {
61+
func (bf *BPFFS) Token() (*sys.FD, error) {
6262
if bf.tokenFd != nil {
6363
return bf.tokenFd.Dup()
6464
}

btf/btf_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"github.com/cilium/ebpf/internal/testutils"
1919
)
2020

21+
const NotBPFTokenFd int32 = -1
22+
2123
func vmlinuxSpec(tb testing.TB) *Spec {
2224
tb.Helper()
2325

@@ -306,7 +308,7 @@ func TestLoadSpecFromElf(t *testing.T) {
306308
func TestVerifierError(t *testing.T) {
307309
b, err := NewBuilder([]Type{&Int{Encoding: 255}})
308310
qt.Assert(t, qt.IsNil(err))
309-
_, err = NewHandle(b)
311+
_, err = NewHandle(b, NotBPFTokenFd)
310312
testutils.SkipIfNotSupported(t, err)
311313
var ve *internal.VerifierError
312314
if !errors.As(err, &ve) {

btf/feature.go

Lines changed: 134 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -11,130 +11,147 @@ import (
1111

1212
// haveBTF attempts to load a BTF blob containing an Int. It should pass on any
1313
// kernel that supports BPF_BTF_LOAD.
14-
var haveBTF = internal.NewFeatureTest("BTF", func() error {
15-
// 0-length anonymous integer
16-
err := probeBTF(&Int{})
17-
if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {
18-
return internal.ErrNotSupported
19-
}
20-
return err
21-
}, "4.18")
14+
var haveBTF = internal.NewFeatureTest("BTF",
15+
func(opts ...internal.FeatureTestOption) error {
16+
// 0-length anonymous integer
17+
o := internal.BuildOptions(opts...)
18+
19+
err := probeBTF(&Int{}, o.BpffsTokenFd)
20+
if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {
21+
return internal.ErrNotSupported
22+
}
23+
return err
24+
},
25+
"4.18",
26+
)
2227

2328
// haveMapBTF attempts to load a minimal BTF blob containing a Var. It is
2429
// used as a proxy for .bss, .data and .rodata map support, which generally
2530
// come with a Var and Datasec. These were introduced in Linux 5.2.
26-
var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", func() error {
27-
if err := haveBTF(); err != nil {
31+
var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)",
32+
func(opts ...internal.FeatureTestOption) error {
33+
if err := haveBTF(opts...); err != nil {
34+
return err
35+
}
36+
37+
v := &Var{
38+
Name: "a",
39+
Type: &Pointer{(*Void)(nil)},
40+
}
41+
42+
o := internal.BuildOptions(opts...)
43+
err := probeBTF(v, o.BpffsTokenFd)
44+
if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {
45+
// Treat both EINVAL and EPERM as not supported: creating the map may still
46+
// succeed without Btf* attrs.
47+
return internal.ErrNotSupported
48+
}
2849
return err
29-
}
30-
31-
v := &Var{
32-
Name: "a",
33-
Type: &Pointer{(*Void)(nil)},
34-
}
35-
36-
err := probeBTF(v)
37-
if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {
38-
// Treat both EINVAL and EPERM as not supported: creating the map may still
39-
// succeed without Btf* attrs.
40-
return internal.ErrNotSupported
41-
}
42-
return err
43-
}, "5.2")
50+
}, "5.2")
4451

4552
// haveProgBTF attempts to load a BTF blob containing a Func and FuncProto. It
4653
// is used as a proxy for ext_info (func_info) support, which depends on
4754
// Func(Proto) by definition.
48-
var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", func() error {
49-
if err := haveBTF(); err != nil {
55+
var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)",
56+
func(opts ...internal.FeatureTestOption) error {
57+
if err := haveBTF(opts...); err != nil {
58+
return err
59+
}
60+
61+
fn := &Func{
62+
Name: "a",
63+
Type: &FuncProto{Return: (*Void)(nil)},
64+
}
65+
66+
o := internal.BuildOptions(opts...)
67+
err := probeBTF(fn, o.BpffsTokenFd)
68+
if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {
69+
return internal.ErrNotSupported
70+
}
5071
return err
51-
}
52-
53-
fn := &Func{
54-
Name: "a",
55-
Type: &FuncProto{Return: (*Void)(nil)},
56-
}
57-
58-
err := probeBTF(fn)
59-
if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {
60-
return internal.ErrNotSupported
61-
}
62-
return err
63-
}, "5.0")
64-
65-
var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", func() error {
66-
if err := haveProgBTF(); err != nil {
72+
}, "5.0")
73+
74+
var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage",
75+
func(opts ...internal.FeatureTestOption) error {
76+
if err := haveProgBTF(opts...); err != nil {
77+
return err
78+
}
79+
80+
fn := &Func{
81+
Name: "a",
82+
Type: &FuncProto{Return: (*Void)(nil)},
83+
Linkage: GlobalFunc,
84+
}
85+
86+
o := internal.BuildOptions(opts...)
87+
err := probeBTF(fn, o.BpffsTokenFd)
88+
if errors.Is(err, unix.EINVAL) {
89+
return internal.ErrNotSupported
90+
}
6791
return err
68-
}
69-
70-
fn := &Func{
71-
Name: "a",
72-
Type: &FuncProto{Return: (*Void)(nil)},
73-
Linkage: GlobalFunc,
74-
}
75-
76-
err := probeBTF(fn)
77-
if errors.Is(err, unix.EINVAL) {
78-
return internal.ErrNotSupported
79-
}
80-
return err
81-
}, "5.6")
82-
83-
var haveDeclTags = internal.NewFeatureTest("BTF decl tags", func() error {
84-
if err := haveBTF(); err != nil {
92+
}, "5.6")
93+
94+
var haveDeclTags = internal.NewFeatureTest("BTF decl tags",
95+
func(opts ...internal.FeatureTestOption) error {
96+
if err := haveBTF(opts...); err != nil {
97+
return err
98+
}
99+
100+
t := &Typedef{
101+
Name: "a",
102+
Type: &Int{},
103+
Tags: []string{"a"},
104+
}
105+
106+
o := internal.BuildOptions(opts...)
107+
err := probeBTF(t, o.BpffsTokenFd)
108+
if errors.Is(err, unix.EINVAL) {
109+
return internal.ErrNotSupported
110+
}
85111
return err
86-
}
87-
88-
t := &Typedef{
89-
Name: "a",
90-
Type: &Int{},
91-
Tags: []string{"a"},
92-
}
93-
94-
err := probeBTF(t)
95-
if errors.Is(err, unix.EINVAL) {
96-
return internal.ErrNotSupported
97-
}
98-
return err
99-
}, "5.16")
100-
101-
var haveTypeTags = internal.NewFeatureTest("BTF type tags", func() error {
102-
if err := haveBTF(); err != nil {
112+
}, "5.16")
113+
114+
var haveTypeTags = internal.NewFeatureTest("BTF type tags",
115+
func(opts ...internal.FeatureTestOption) error {
116+
if err := haveBTF(opts...); err != nil {
117+
return err
118+
}
119+
120+
t := &TypeTag{
121+
Type: &Int{},
122+
Value: "a",
123+
}
124+
125+
o := internal.BuildOptions(opts...)
126+
err := probeBTF(t, o.BpffsTokenFd)
127+
if errors.Is(err, unix.EINVAL) {
128+
return internal.ErrNotSupported
129+
}
103130
return err
104-
}
105-
106-
t := &TypeTag{
107-
Type: &Int{},
108-
Value: "a",
109-
}
110-
111-
err := probeBTF(t)
112-
if errors.Is(err, unix.EINVAL) {
113-
return internal.ErrNotSupported
114-
}
115-
return err
116-
}, "5.17")
117-
118-
var haveEnum64 = internal.NewFeatureTest("ENUM64", func() error {
119-
if err := haveBTF(); err != nil {
131+
}, "5.17")
132+
133+
var haveEnum64 = internal.NewFeatureTest("ENUM64",
134+
func(opts ...internal.FeatureTestOption) error {
135+
if err := haveBTF(opts...); err != nil {
136+
return err
137+
}
138+
139+
enum := &Enum{
140+
Size: 8,
141+
Values: []EnumValue{
142+
{"TEST", math.MaxUint32 + 1},
143+
},
144+
}
145+
146+
o := internal.BuildOptions(opts...)
147+
err := probeBTF(enum, o.BpffsTokenFd)
148+
if errors.Is(err, unix.EINVAL) {
149+
return internal.ErrNotSupported
150+
}
120151
return err
121-
}
152+
}, "6.0")
122153

123-
enum := &Enum{
124-
Size: 8,
125-
Values: []EnumValue{
126-
{"TEST", math.MaxUint32 + 1},
127-
},
128-
}
129-
130-
err := probeBTF(enum)
131-
if errors.Is(err, unix.EINVAL) {
132-
return internal.ErrNotSupported
133-
}
134-
return err
135-
}, "6.0")
136-
137-
func probeBTF(typ Type) error {
154+
func probeBTF(typ Type, tokenFd int32) error {
138155
b, err := NewBuilder([]Type{typ})
139156
if err != nil {
140157
return err
@@ -145,11 +162,17 @@ func probeBTF(typ Type) error {
145162
return err
146163
}
147164

148-
fd, err := sys.BtfLoad(&sys.BtfLoadAttr{
165+
attr := &sys.BtfLoadAttr{
149166
Btf: sys.SlicePointer(buf),
150167
BtfSize: uint32(len(buf)),
151-
})
168+
}
169+
170+
if tokenFd > 0 {
171+
attr.BtfTokenFd = tokenFd
172+
attr.BtfFlags |= sys.BPF_F_TOKEN_FD
173+
}
152174

175+
fd, err := sys.BtfLoad(attr)
153176
if err == nil {
154177
fd.Close()
155178
}

btf/handle.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,22 @@ type Handle struct {
2525
// NewHandle loads the contents of a [Builder] into the kernel.
2626
//
2727
// Returns an error wrapping ErrNotSupported if the kernel doesn't support BTF.
28-
func NewHandle(b *Builder) (*Handle, error) {
28+
func NewHandle(b *Builder, tokenFd int32) (*Handle, error) {
2929
small := getByteSlice()
3030
defer putByteSlice(small)
3131

32-
buf, err := b.Marshal(*small, KernelMarshalOptions())
32+
buf, err := b.Marshal(*small, KernelMarshalOptions(tokenFd))
3333
if err != nil {
3434
return nil, fmt.Errorf("marshal BTF: %w", err)
3535
}
3636

37-
return NewHandleFromRawBTF(buf)
37+
return NewHandleFromRawBTF(buf, tokenFd)
3838
}
3939

4040
// NewHandleFromRawBTF loads raw BTF into the kernel.
4141
//
4242
// Returns an error wrapping ErrNotSupported if the kernel doesn't support BTF.
43-
func NewHandleFromRawBTF(btf []byte) (*Handle, error) {
43+
func NewHandleFromRawBTF(btf []byte, tokenFd int32) (*Handle, error) {
4444
const minLogSize = 64 * 1024
4545

4646
if platform.IsWindows {
@@ -56,6 +56,11 @@ func NewHandleFromRawBTF(btf []byte) (*Handle, error) {
5656
BtfSize: uint32(len(btf)),
5757
}
5858

59+
if tokenFd > 0 {
60+
attr.BtfTokenFd = tokenFd
61+
attr.BtfFlags |= sys.BPF_F_TOKEN_FD
62+
}
63+
5964
var (
6065
logBuf []byte
6166
err error
@@ -99,7 +104,7 @@ func NewHandleFromRawBTF(btf []byte) (*Handle, error) {
99104
attr.BtfLogLevel = 1
100105
}
101106

102-
if err := haveBTF(); err != nil {
107+
if err := haveBTF(internal.WithToken(tokenFd)); err != nil {
103108
return nil, err
104109
}
105110

btf/marshal.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ type MarshalOptions struct {
2929
}
3030

3131
// KernelMarshalOptions will generate BTF suitable for the current kernel.
32-
func KernelMarshalOptions() *MarshalOptions {
32+
func KernelMarshalOptions(tokenFd int32) *MarshalOptions {
3333
return &MarshalOptions{
3434
Order: internal.NativeEndian,
35-
StripFuncLinkage: haveFuncLinkage() != nil,
36-
ReplaceDeclTags: haveDeclTags() != nil,
37-
ReplaceTypeTags: haveTypeTags() != nil,
38-
ReplaceEnum64: haveEnum64() != nil,
35+
StripFuncLinkage: haveFuncLinkage(internal.WithToken(tokenFd)) != nil,
36+
ReplaceDeclTags: haveDeclTags(internal.WithToken(tokenFd)) != nil,
37+
ReplaceTypeTags: haveTypeTags(internal.WithToken(tokenFd)) != nil,
38+
ReplaceEnum64: haveEnum64(internal.WithToken(tokenFd)) != nil,
3939
PreventNoTypeFound: true, // All current kernels require this.
4040
}
4141
}
@@ -667,7 +667,7 @@ func (e *encoder) deflateVarSecinfos(buf []byte, vars []VarSecinfo) ([]byte, err
667667
//
668668
// The function is intended for the use of the ebpf package and may be removed
669669
// at any point in time.
670-
func MarshalMapKV(key, value Type) (_ *Handle, keyID, valueID TypeID, err error) {
670+
func MarshalMapKV(key, value Type, tokenFd int32) (_ *Handle, keyID, valueID TypeID, err error) {
671671
var b Builder
672672

673673
if key != nil {
@@ -684,11 +684,11 @@ func MarshalMapKV(key, value Type) (_ *Handle, keyID, valueID TypeID, err error)
684684
}
685685
}
686686

687-
handle, err := NewHandle(&b)
687+
handle, err := NewHandle(&b, tokenFd)
688688
if err != nil {
689689
// Check for 'full' map BTF support, since kernels between 4.18 and 5.2
690690
// already support BTF blobs for maps without Var or Datasec just fine.
691-
if err := haveMapBTF(); err != nil {
691+
if err := haveMapBTF(internal.WithToken(tokenFd)); err != nil {
692692
return nil, 0, 0, err
693693
}
694694
}

0 commit comments

Comments
 (0)