Codebase list golang-github-cilium-ebpf / 430b22a
New upstream version 0.0~git20200810.c7f1234 Shengjing Zhu 3 years ago
83 changed file(s) with 3262 addition(s) and 1086 deletion(s). Raw diff Collapse all Expand all
2929 - env_var: GO_VERSION
3030 values: [ "1.13", "1.14" ]
3131 - env_var: KERNEL_VERSION
32 values: ["5.4.5", "4.19.81", "4.9.198"]
32 values: ["5.4", "4.19", "4.9"]
3333 commands:
3434 - sem-version go $GO_VERSION
35 - timeout -s KILL 90s ./run-tests.sh $KERNEL_VERSION
35 - timeout -s KILL 600s ./run-tests.sh $KERNEL_VERSION
22 import (
33 "bufio"
44 "bytes"
5 "errors"
56 "fmt"
67 "io"
78 "os"
89 "syscall"
910
1011 "github.com/cilium/ebpf/internal"
11
12 "golang.org/x/xerrors"
1312 )
1413
1514 // MapABI are the attributes of a Map which are available across all supported kernels.
3433 func newMapABIFromFd(fd *internal.FD) (string, *MapABI, error) {
3534 info, err := bpfGetMapInfoByFD(fd)
3635 if err != nil {
37 if xerrors.Is(err, syscall.EINVAL) {
36 if errors.Is(err, syscall.EINVAL) {
3837 abi, err := newMapABIFromProc(fd)
3938 return "", abi, err
4039 }
9796 func newProgramABIFromFd(fd *internal.FD) (string, *ProgramABI, error) {
9897 info, err := bpfGetProgInfoByFD(fd)
9998 if err != nil {
100 if xerrors.Is(err, syscall.EINVAL) {
99 if errors.Is(err, syscall.EINVAL) {
101100 return newProgramABIFromProc(fd)
102101 }
103102
126125 "prog_type": &abi.Type,
127126 "prog_tag": &name,
128127 })
129 if xerrors.Is(err, errMissingFields) {
128 if errors.Is(err, errMissingFields) {
130129 return "", nil, &internal.UnsupportedFeatureError{
131130 Name: "reading ABI from /proc/self/fdinfo",
132131 MinimumVersion: internal.Version{4, 11, 0},
152151 defer fh.Close()
153152
154153 if err := scanFdInfoReader(fh, fields); err != nil {
155 return xerrors.Errorf("%s: %w", fh.Name(), err)
154 return fmt.Errorf("%s: %w", fh.Name(), err)
156155 }
157156 return nil
158157 }
159158
160 var errMissingFields = xerrors.New("missing fields")
159 var errMissingFields = errors.New("missing fields")
161160
162161 func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
163162 var (
178177 }
179178
180179 if n, err := fmt.Fscanln(bytes.NewReader(parts[1]), field); err != nil || n != 1 {
181 return xerrors.Errorf("can't parse field %s: %v", name, err)
180 return fmt.Errorf("can't parse field %s: %v", name, err)
182181 }
183182
184183 scanned++
11
22 import (
33 "encoding/binary"
4 "errors"
45 "fmt"
56 "io"
67 "math"
78 "strings"
8
9 "golang.org/x/xerrors"
109 )
1110
1211 // InstructionSize is the size of a BPF instruction in bytes
3837 }
3938
4039 ins.OpCode = bi.OpCode
41 ins.Dst = bi.Registers.Dst()
42 ins.Src = bi.Registers.Src()
4340 ins.Offset = bi.Offset
4441 ins.Constant = int64(bi.Constant)
42 ins.Dst, ins.Src, err = bi.Registers.Unmarshal(bo)
43 if err != nil {
44 return 0, fmt.Errorf("can't unmarshal registers: %s", err)
45 }
4546
4647 if !bi.OpCode.isDWordLoad() {
4748 return InstructionSize, nil
5051 var bi2 bpfInstruction
5152 if err := binary.Read(r, bo, &bi2); err != nil {
5253 // No Wrap, to avoid io.EOF clash
53 return 0, xerrors.New("64bit immediate is missing second half")
54 return 0, errors.New("64bit immediate is missing second half")
5455 }
5556 if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 {
56 return 0, xerrors.New("64bit immediate has non-zero fields")
57 return 0, errors.New("64bit immediate has non-zero fields")
5758 }
5859 ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant)))
5960
6364 // Marshal encodes a BPF instruction.
6465 func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) {
6566 if ins.OpCode == InvalidOpCode {
66 return 0, xerrors.New("invalid opcode")
67 return 0, errors.New("invalid opcode")
6768 }
6869
6970 isDWordLoad := ins.OpCode.isDWordLoad()
7475 cons = int32(uint32(ins.Constant))
7576 }
7677
78 regs, err := newBPFRegisters(ins.Dst, ins.Src, bo)
79 if err != nil {
80 return 0, fmt.Errorf("can't marshal registers: %s", err)
81 }
82
7783 bpfi := bpfInstruction{
7884 ins.OpCode,
79 newBPFRegisters(ins.Dst, ins.Src),
85 regs,
8086 ins.Offset,
8187 cons,
8288 }
105111 // Returns an error if the instruction doesn't load a map.
106112 func (ins *Instruction) RewriteMapPtr(fd int) error {
107113 if !ins.OpCode.isDWordLoad() {
108 return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode)
114 return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
109115 }
110116
111117 if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue {
112 return xerrors.New("not a load from a map")
118 return errors.New("not a load from a map")
113119 }
114120
115121 // Preserve the offset value for direct map loads.
128134 // Returns an error if the instruction is not a direct load.
129135 func (ins *Instruction) RewriteMapOffset(offset uint32) error {
130136 if !ins.OpCode.isDWordLoad() {
131 return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode)
137 return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
132138 }
133139
134140 if ins.Src != PseudoMapValue {
135 return xerrors.New("not a direct load from a map")
141 return errors.New("not a direct load from a map")
136142 }
137143
138144 fd := uint64(ins.Constant) & math.MaxUint32
243249 // Returns an error if the symbol isn't used, see IsUnreferencedSymbol.
244250 func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
245251 if symbol == "" {
246 return xerrors.New("empty symbol")
252 return errors.New("empty symbol")
247253 }
248254
249255 found := false
278284 }
279285
280286 if _, ok := offsets[ins.Symbol]; ok {
281 return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol)
287 return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol)
282288 }
283289
284290 offsets[ins.Symbol] = i
316322 }
317323
318324 if _, ok := symbols[ins.Symbol]; ok {
319 return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol)
325 return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol)
320326 }
321327
322328 symbols[ins.Symbol] = currentPos
397403 // Rewrite bpf to bpf call
398404 offset, ok := absoluteOffsets[ins.Reference]
399405 if !ok {
400 return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
406 return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
401407 }
402408
403409 ins.Constant = int64(offset - num - 1)
406412 // Rewrite jump to label
407413 offset, ok := absoluteOffsets[ins.Reference]
408414 if !ok {
409 return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
415 return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
410416 }
411417
412418 ins.Offset = int16(offset - num - 1)
414420
415421 n, err := ins.Marshal(w, bo)
416422 if err != nil {
417 return xerrors.Errorf("instruction %d: %w", i, err)
423 return fmt.Errorf("instruction %d: %w", i, err)
418424 }
419425
420426 num += int(n / InstructionSize)
431437
432438 type bpfRegisters uint8
433439
434 func newBPFRegisters(dst, src Register) bpfRegisters {
435 return bpfRegisters((src << 4) | (dst & 0xF))
436 }
437
438 func (r bpfRegisters) Dst() Register {
439 return Register(r & 0xF)
440 }
441
442 func (r bpfRegisters) Src() Register {
443 return Register(r >> 4)
440 func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
441 switch bo {
442 case binary.LittleEndian:
443 return bpfRegisters((src << 4) | (dst & 0xF)), nil
444 case binary.BigEndian:
445 return bpfRegisters((dst << 4) | (src & 0xF)), nil
446 default:
447 return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
448 }
449 }
450
451 func (r bpfRegisters) Unmarshal(bo binary.ByteOrder) (dst, src Register, err error) {
452 switch bo {
453 case binary.LittleEndian:
454 return Register(r & 0xF), Register(r >> 4), nil
455 case binary.BigEndian:
456 return Register(r >> 4), Register(r & 0xf), nil
457 default:
458 return 0, 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
459 }
444460 }
445461
446462 type unreferencedSymbolError struct {
178178 // 1: LdImmDW dst: r0 imm: 42
179179 // 3: Exit
180180 }
181
182 func TestReadSrcDst(t *testing.T) {
183 testSrcDstProg := []byte{
184 // on little-endian: r0 = r1
185 // on big-endian: be: r1 = r0
186 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187 }
188
189 testcases := []struct {
190 bo binary.ByteOrder
191 dst, src Register
192 }{
193 {binary.BigEndian, R1, R0},
194 {binary.LittleEndian, R0, R1},
195 }
196
197 for _, tc := range testcases {
198 t.Run(tc.bo.String(), func(t *testing.T) {
199 var ins Instruction
200 _, err := ins.Unmarshal(bytes.NewReader(testSrcDstProg), tc.bo)
201 if err != nil {
202 t.Fatal(err)
203 }
204 if ins.Dst != tc.dst {
205 t.Errorf("Expected destination to be %v, got %v", tc.dst, ins.Dst)
206 }
207 if ins.Src != tc.src {
208 t.Errorf("Expected source to be %v, got %v", tc.src, ins.Src)
209 }
210 })
211 }
212 }
224224 }
225225
226226 default:
227 fmt.Fprintf(&f, "%#x", op)
227 fmt.Fprintf(&f, "OpCode(%#x)", uint8(op))
228228 }
229229
230230 return f.String()
00 package ebpf
11
22 import (
3 "errors"
4 "fmt"
35 "math"
6 "reflect"
7 "strings"
48
59 "github.com/cilium/ebpf/asm"
610 "github.com/cilium/ebpf/internal"
711 "github.com/cilium/ebpf/internal/btf"
8 "golang.org/x/xerrors"
912 )
1013
1114 // CollectionOptions control loading a collection into the kernel.
6366 // Not all programs need to use the map
6467
6568 default:
66 return xerrors.Errorf("program %s: %w", progName, err)
69 return fmt.Errorf("program %s: %w", progName, err)
6770 }
6871 }
6972
7073 if !seen {
71 return xerrors.Errorf("map %s not referenced by any programs", symbol)
74 return fmt.Errorf("map %s not referenced by any programs", symbol)
7275 }
7376
7477 // Prevent NewCollection from creating rewritten maps
9598 func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error {
9699 rodata := cs.Maps[".rodata"]
97100 if rodata == nil {
98 return xerrors.New("missing .rodata section")
101 return errors.New("missing .rodata section")
99102 }
100103
101104 if rodata.BTF == nil {
102 return xerrors.New(".rodata section has no BTF")
105 return errors.New(".rodata section has no BTF")
103106 }
104107
105108 if n := len(rodata.Contents); n != 1 {
106 return xerrors.Errorf("expected one key in .rodata, found %d", n)
109 return fmt.Errorf("expected one key in .rodata, found %d", n)
107110 }
108111
109112 kv := rodata.Contents[0]
110113 value, ok := kv.Value.([]byte)
111114 if !ok {
112 return xerrors.Errorf("first value in .rodata is %T not []byte", kv.Value)
115 return fmt.Errorf("first value in .rodata is %T not []byte", kv.Value)
113116 }
114117
115118 buf := make([]byte, len(value))
122125
123126 rodata.Contents[0] = MapKV{kv.Key, buf}
124127 return nil
128 }
129
130 // Assign the contents of a collection spec to a struct.
131 //
132 // This function is a short-cut to manually checking the presence
133 // of maps and programs in a collection spec.
134 //
135 // The argument to must be a pointer to a struct. A field of the
136 // struct is updated with values from Programs or Maps if it
137 // has an `ebpf` tag and its type is *ProgramSpec or *MapSpec.
138 // The tag gives the name of the program or map as found in
139 // the CollectionSpec.
140 //
141 // struct {
142 // Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"`
143 // Bar *ebpf.MapSpec `ebpf:"bar_map"`
144 // Ignored int
145 // }
146 //
147 // Returns an error if any of the fields can't be found, or
148 // if the same map or program is assigned multiple times.
149 func (cs *CollectionSpec) Assign(to interface{}) error {
150 valueOf := func(typ reflect.Type, name string) (reflect.Value, error) {
151 switch typ {
152 case reflect.TypeOf((*ProgramSpec)(nil)):
153 p := cs.Programs[name]
154 if p == nil {
155 return reflect.Value{}, fmt.Errorf("missing program %q", name)
156 }
157 return reflect.ValueOf(p), nil
158 case reflect.TypeOf((*MapSpec)(nil)):
159 m := cs.Maps[name]
160 if m == nil {
161 return reflect.Value{}, fmt.Errorf("missing map %q", name)
162 }
163 return reflect.ValueOf(m), nil
164 default:
165 return reflect.Value{}, fmt.Errorf("unsupported type %s", typ)
166 }
167 }
168
169 return assignValues(to, valueOf)
170 }
171
172 // LoadAndAssign creates a collection from a spec, and assigns it to a struct.
173 //
174 // See Collection.Assign for details.
175 func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error {
176 if opts == nil {
177 opts = &CollectionOptions{}
178 }
179
180 coll, err := NewCollectionWithOptions(cs, *opts)
181 if err != nil {
182 return err
183 }
184 defer coll.Close()
185
186 return coll.Assign(to)
125187 }
126188
127189 // Collection is a collection of Programs and Maps associated
184246 var handle *btf.Handle
185247 if mapSpec.BTF != nil {
186248 handle, err = loadBTF(btf.MapSpec(mapSpec.BTF))
187 if err != nil && !xerrors.Is(err, btf.ErrNotSupported) {
249 if err != nil && !errors.Is(err, btf.ErrNotSupported) {
188250 return nil, err
189251 }
190252 }
191253
192254 m, err := newMapWithBTF(mapSpec, handle)
193255 if err != nil {
194 return nil, xerrors.Errorf("map %s: %w", mapName, err)
256 return nil, fmt.Errorf("map %s: %w", mapName, err)
195257 }
196258 maps[mapName] = m
197259 }
215277
216278 m := maps[ins.Reference]
217279 if m == nil {
218 return nil, xerrors.Errorf("program %s: missing map %s", progName, ins.Reference)
280 return nil, fmt.Errorf("program %s: missing map %s", progName, ins.Reference)
219281 }
220282
221283 fd := m.FD()
222284 if fd < 0 {
223 return nil, xerrors.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd)
285 return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd)
224286 }
225287 if err := ins.RewriteMapPtr(m.FD()); err != nil {
226 return nil, xerrors.Errorf("progam %s: map %s: %w", progName, ins.Reference, err)
288 return nil, fmt.Errorf("progam %s: map %s: %w", progName, ins.Reference, err)
227289 }
228290 }
229291
230292 var handle *btf.Handle
231293 if progSpec.BTF != nil {
232294 handle, err = loadBTF(btf.ProgramSpec(progSpec.BTF))
233 if err != nil && !xerrors.Is(err, btf.ErrNotSupported) {
295 if err != nil && !errors.Is(err, btf.ErrNotSupported) {
234296 return nil, err
235297 }
236298 }
237299
238300 prog, err := newProgramWithBTF(progSpec, handle, opts.Programs)
239301 if err != nil {
240 return nil, xerrors.Errorf("program %s: %w", progName, err)
302 return nil, fmt.Errorf("program %s: %w", progName, err)
241303 }
242304 progs[progName] = prog
243305 }
290352 delete(coll.Programs, name)
291353 return p
292354 }
355
356 // Assign the contents of a collection to a struct.
357 //
358 // `to` must be a pointer to a struct like the following:
359 //
360 // struct {
361 // Foo *ebpf.Program `ebpf:"xdp_foo"`
362 // Bar *ebpf.Map `ebpf:"bar_map"`
363 // Ignored int
364 // }
365 //
366 // See CollectionSpec.Assign for the semantics of this function.
367 //
368 // DetachMap and DetachProgram is invoked for all assigned elements
369 // if the function is successful.
370 func (coll *Collection) Assign(to interface{}) error {
371 assignedMaps := make(map[string]struct{})
372 assignedPrograms := make(map[string]struct{})
373 valueOf := func(typ reflect.Type, name string) (reflect.Value, error) {
374 switch typ {
375 case reflect.TypeOf((*Program)(nil)):
376 p := coll.Programs[name]
377 if p == nil {
378 return reflect.Value{}, fmt.Errorf("missing program %q", name)
379 }
380 assignedPrograms[name] = struct{}{}
381 return reflect.ValueOf(p), nil
382 case reflect.TypeOf((*Map)(nil)):
383 m := coll.Maps[name]
384 if m == nil {
385 return reflect.Value{}, fmt.Errorf("missing map %q", name)
386 }
387 assignedMaps[name] = struct{}{}
388 return reflect.ValueOf(m), nil
389 default:
390 return reflect.Value{}, fmt.Errorf("unsupported type %s", typ)
391 }
392 }
393
394 if err := assignValues(to, valueOf); err != nil {
395 return err
396 }
397
398 for name := range assignedPrograms {
399 coll.DetachProgram(name)
400 }
401
402 for name := range assignedMaps {
403 coll.DetachMap(name)
404 }
405
406 return nil
407 }
408
409 func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Value, error)) error {
410 v := reflect.ValueOf(to)
411 if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
412 return fmt.Errorf("%T is not a pointer to a struct", to)
413 }
414
415 type elem struct {
416 typ reflect.Type
417 name string
418 }
419
420 var (
421 s = v.Elem()
422 sT = s.Type()
423 assignedTo = make(map[elem]string)
424 )
425 for i := 0; i < sT.NumField(); i++ {
426 field := sT.Field(i)
427
428 name := field.Tag.Get("ebpf")
429 if name == "" {
430 continue
431 }
432 if strings.Contains(name, ",") {
433 return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name)
434 }
435
436 e := elem{field.Type, name}
437 if assignedField := assignedTo[e]; assignedField != "" {
438 return fmt.Errorf("field %s: %q was already assigned to %s", field.Name, name, assignedField)
439 }
440
441 value, err := valueOf(field.Type, name)
442 if err != nil {
443 return fmt.Errorf("field %s: %w", field.Name, err)
444 }
445
446 fieldValue := s.Field(i)
447 if !fieldValue.CanSet() {
448 return fmt.Errorf("can't set value of field %s", field.Name)
449 }
450
451 fieldValue.Set(value)
452 assignedTo[e] = field.Name
453 }
454
455 return nil
456 }
00 package ebpf
11
22 import (
3 "fmt"
34 "testing"
45
56 "github.com/cilium/ebpf/asm"
152153 t.Fatal("new / override map not used")
153154 }
154155 }
156
157 func TestCollectionAssign(t *testing.T) {
158 var specs struct {
159 Program *ProgramSpec `ebpf:"prog1"`
160 Map *MapSpec `ebpf:"map1"`
161 }
162
163 mapSpec := &MapSpec{
164 Type: Array,
165 KeySize: 4,
166 ValueSize: 4,
167 MaxEntries: 1,
168 }
169 progSpec := &ProgramSpec{
170 Type: SocketFilter,
171 Instructions: asm.Instructions{
172 asm.LoadImm(asm.R0, 0, asm.DWord),
173 asm.Return(),
174 },
175 License: "MIT",
176 }
177
178 cs := &CollectionSpec{
179 Maps: map[string]*MapSpec{
180 "map1": mapSpec,
181 },
182 Programs: map[string]*ProgramSpec{
183 "prog1": progSpec,
184 },
185 }
186
187 if err := cs.Assign(&specs); err != nil {
188 t.Fatal("Can't assign spec:", err)
189 }
190
191 if specs.Program != progSpec {
192 t.Fatalf("Expected Program to be %p, got %p", progSpec, specs.Program)
193 }
194
195 if specs.Map != mapSpec {
196 t.Fatalf("Expected Map to be %p, got %p", mapSpec, specs.Map)
197 }
198
199 if err := cs.Assign(new(int)); err == nil {
200 t.Fatal("Assign allows to besides *struct")
201 }
202
203 if err := cs.Assign(new(struct{ Foo int })); err != nil {
204 t.Fatal("Assign doesn't ignore untagged fields")
205 }
206
207 unexported := new(struct {
208 foo *MapSpec `ebpf:"map1"`
209 })
210
211 if err := cs.Assign(unexported); err == nil {
212 t.Error("Assign should return an error on unexported fields")
213 }
214
215 coll, err := NewCollection(cs)
216 if err != nil {
217 t.Fatal(err)
218 }
219 defer coll.Close()
220
221 var objs struct {
222 Program *Program `ebpf:"prog1"`
223 Map *Map `ebpf:"map1"`
224 }
225
226 if err := coll.Assign(&objs); err != nil {
227 t.Fatal("Can't Assign objects:", err)
228 }
229 objs.Program.Close()
230 objs.Map.Close()
231
232 if coll.Programs["prog1"] != nil {
233 t.Fatal("Assign doesn't detach Program")
234 }
235
236 if coll.Maps["map1"] != nil {
237 t.Fatal("Assign doesn't detach Map")
238 }
239 }
240
241 func ExampleCollectionSpec_Assign() {
242 spec := &CollectionSpec{
243 Maps: map[string]*MapSpec{
244 "map1": {
245 Type: Array,
246 KeySize: 4,
247 ValueSize: 4,
248 MaxEntries: 1,
249 },
250 },
251 Programs: map[string]*ProgramSpec{
252 "prog1": {
253 Type: SocketFilter,
254 Instructions: asm.Instructions{
255 asm.LoadImm(asm.R0, 0, asm.DWord),
256 asm.Return(),
257 },
258 License: "MIT",
259 },
260 },
261 }
262
263 var specs struct {
264 Program *ProgramSpec `ebpf:"prog1"`
265 Map *MapSpec `ebpf:"map1"`
266 }
267
268 if err := spec.Assign(&specs); err != nil {
269 panic(err)
270 }
271
272 fmt.Println(specs.Program.Type)
273 fmt.Println(specs.Map.Type)
274
275 // Output: SocketFilter
276 // Array
277 }
278
279 func ExampleCollectionSpec_LoadAndAssign() {
280 spec := &CollectionSpec{
281 Maps: map[string]*MapSpec{
282 "map1": {
283 Type: Array,
284 KeySize: 4,
285 ValueSize: 4,
286 MaxEntries: 1,
287 },
288 },
289 Programs: map[string]*ProgramSpec{
290 "prog1": {
291 Type: SocketFilter,
292 Instructions: asm.Instructions{
293 asm.LoadImm(asm.R0, 0, asm.DWord),
294 asm.Return(),
295 },
296 License: "MIT",
297 },
298 },
299 }
300
301 var objs struct {
302 Program *Program `ebpf:"prog1"`
303 Map *Map `ebpf:"map1"`
304 }
305
306 if err := spec.LoadAndAssign(&objs, nil); err != nil {
307 panic(err)
308 }
309
310 fmt.Println(objs.Program.ABI().Type)
311 fmt.Println(objs.Map.ABI().Type)
312
313 // Output: SocketFilter
314 // Array
315 }
316
317 func ExampleCollection_Assign() {
318 coll, err := NewCollection(&CollectionSpec{
319 Maps: map[string]*MapSpec{
320 "map1": {
321 Type: Array,
322 KeySize: 4,
323 ValueSize: 4,
324 MaxEntries: 1,
325 },
326 },
327 Programs: map[string]*ProgramSpec{
328 "prog1": {
329 Type: SocketFilter,
330 Instructions: asm.Instructions{
331 asm.LoadImm(asm.R0, 0, asm.DWord),
332 asm.Return(),
333 },
334 License: "MIT",
335 },
336 },
337 })
338 if err != nil {
339 panic(err)
340 }
341
342 var objs struct {
343 Program *Program `ebpf:"prog1"`
344 Map *Map `ebpf:"map1"`
345 }
346
347 if err := coll.Assign(&objs); err != nil {
348 panic(err)
349 }
350
351 fmt.Println(objs.Program.ABI().Type)
352 fmt.Println(objs.Map.ABI().Type)
353
354 // Output: SocketFilter
355 // Array
356 }
1111 // eBPF code should be compiled ahead of time using clang, and shipped with
1212 // your application as any other resource.
1313 //
14 // This package doesn't include code required to attach eBPF to Linux
15 // subsystems, since this varies per subsystem.
14 // Use the link subpackage to attach a loaded program to a hook in the kernel.
1615 package ebpf
33 "bytes"
44 "debug/elf"
55 "encoding/binary"
6 "errors"
7 "fmt"
68 "io"
79 "math"
810 "os"
1214 "github.com/cilium/ebpf/internal"
1315 "github.com/cilium/ebpf/internal/btf"
1416 "github.com/cilium/ebpf/internal/unix"
15
16 "golang.org/x/xerrors"
1717 )
1818
1919 type elfCode struct {
3434
3535 spec, err := LoadCollectionSpecFromReader(f)
3636 if err != nil {
37 return nil, xerrors.Errorf("file %s: %w", file, err)
37 return nil, fmt.Errorf("file %s: %w", file, err)
3838 }
3939 return spec, nil
4040 }
4949
5050 symbols, err := f.Symbols()
5151 if err != nil {
52 return nil, xerrors.Errorf("load symbols: %v", err)
52 return nil, fmt.Errorf("load symbols: %v", err)
5353 }
5454
5555 ec := &elfCode{f, symbols, symbolsPerSection(symbols), "", 0}
7878 dataSections[elf.SectionIndex(i)] = sec
7979 case sec.Type == elf.SHT_REL:
8080 if int(sec.Info) >= len(ec.Sections) {
81 return nil, xerrors.Errorf("found relocation section %v for missing section %v", i, sec.Info)
81 return nil, fmt.Errorf("found relocation section %v for missing section %v", i, sec.Info)
8282 }
8383
8484 // Store relocations under the section index of the target
8585 idx := elf.SectionIndex(sec.Info)
8686 if relSections[idx] != nil {
87 return nil, xerrors.Errorf("section %d has multiple relocation sections", sec.Info)
87 return nil, fmt.Errorf("section %d has multiple relocation sections", sec.Info)
8888 }
8989 relSections[idx] = sec
9090 case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0:
9494
9595 ec.license, err = loadLicense(licenseSection)
9696 if err != nil {
97 return nil, xerrors.Errorf("load license: %w", err)
97 return nil, fmt.Errorf("load license: %w", err)
9898 }
9999
100100 ec.version, err = loadVersion(versionSection, ec.ByteOrder)
101101 if err != nil {
102 return nil, xerrors.Errorf("load version: %w", err)
102 return nil, fmt.Errorf("load version: %w", err)
103103 }
104104
105105 btfSpec, err := btf.LoadSpecFromReader(rd)
106106 if err != nil {
107 return nil, xerrors.Errorf("load BTF: %w", err)
107 return nil, fmt.Errorf("load BTF: %w", err)
108 }
109
110 relocations, referencedSections, err := ec.loadRelocations(relSections)
111 if err != nil {
112 return nil, fmt.Errorf("load relocations: %w", err)
108113 }
109114
110115 maps := make(map[string]*MapSpec)
111116 if err := ec.loadMaps(maps, mapSections); err != nil {
112 return nil, xerrors.Errorf("load maps: %w", err)
117 return nil, fmt.Errorf("load maps: %w", err)
113118 }
114119
115120 if len(btfMaps) > 0 {
116121 if err := ec.loadBTFMaps(maps, btfMaps, btfSpec); err != nil {
117 return nil, xerrors.Errorf("load BTF maps: %w", err)
122 return nil, fmt.Errorf("load BTF maps: %w", err)
118123 }
119124 }
120125
121126 if len(dataSections) > 0 {
127 for idx := range dataSections {
128 if !referencedSections[idx] {
129 // Prune data sections which are not referenced by any
130 // instructions.
131 delete(dataSections, idx)
132 }
133 }
134
122135 if err := ec.loadDataSections(maps, dataSections, btfSpec); err != nil {
123 return nil, xerrors.Errorf("load data sections: %w", err)
124 }
125 }
126
127 relocations, err := ec.loadRelocations(relSections)
128 if err != nil {
129 return nil, xerrors.Errorf("load relocations: %w", err)
136 return nil, fmt.Errorf("load data sections: %w", err)
137 }
130138 }
131139
132140 progs, err := ec.loadPrograms(progSections, relocations, btfSpec)
133141 if err != nil {
134 return nil, xerrors.Errorf("load programs: %w", err)
142 return nil, fmt.Errorf("load programs: %w", err)
135143 }
136144
137145 return &CollectionSpec{maps, progs}, nil
139147
140148 func loadLicense(sec *elf.Section) (string, error) {
141149 if sec == nil {
142 return "", xerrors.New("missing license section")
143 }
150 return "", nil
151 }
152
144153 data, err := sec.Data()
145154 if err != nil {
146 return "", xerrors.Errorf("section %s: %v", sec.Name, err)
155 return "", fmt.Errorf("section %s: %v", sec.Name, err)
147156 }
148157 return string(bytes.TrimRight(data, "\000")), nil
149158 }
155164
156165 var version uint32
157166 if err := binary.Read(sec.Open(), bo, &version); err != nil {
158 return 0, xerrors.Errorf("section %s: %v", sec.Name, err)
167 return 0, fmt.Errorf("section %s: %v", sec.Name, err)
159168 }
160169 return version, nil
161170 }
162171
163 func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, relocations map[elf.SectionIndex]map[uint64]elf.Symbol, btf *btf.Spec) (map[string]*ProgramSpec, error) {
172 func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, relocations map[elf.SectionIndex]map[uint64]elf.Symbol, btfSpec *btf.Spec) (map[string]*ProgramSpec, error) {
164173 var (
165174 progs []*ProgramSpec
166175 libs []*ProgramSpec
169178 for idx, sec := range progSections {
170179 syms := ec.symbolsPerSection[idx]
171180 if len(syms) == 0 {
172 return nil, xerrors.Errorf("section %v: missing symbols", sec.Name)
181 return nil, fmt.Errorf("section %v: missing symbols", sec.Name)
173182 }
174183
175184 funcSym, ok := syms[0]
176185 if !ok {
177 return nil, xerrors.Errorf("section %v: no label at start", sec.Name)
186 return nil, fmt.Errorf("section %v: no label at start", sec.Name)
178187 }
179188
180189 insns, length, err := ec.loadInstructions(sec, syms, relocations[idx])
181190 if err != nil {
182 return nil, xerrors.Errorf("program %s: can't unmarshal instructions: %w", funcSym.Name, err)
183 }
184
185 progType, attachType := getProgType(sec.Name)
191 return nil, fmt.Errorf("program %s: can't unmarshal instructions: %w", funcSym.Name, err)
192 }
193
194 progType, attachType, attachTo := getProgType(sec.Name)
186195
187196 spec := &ProgramSpec{
188197 Name: funcSym.Name,
189198 Type: progType,
190199 AttachType: attachType,
200 AttachTo: attachTo,
191201 License: ec.license,
192202 KernelVersion: ec.version,
193203 Instructions: insns,
194 }
195
196 if btf != nil {
197 spec.BTF, err = btf.Program(sec.Name, length)
198 if err != nil {
199 return nil, xerrors.Errorf("BTF for section %s (program %s): %w", sec.Name, funcSym.Name, err)
204 ByteOrder: ec.ByteOrder,
205 }
206
207 if btfSpec != nil {
208 spec.BTF, err = btfSpec.Program(sec.Name, length)
209 if err != nil && !errors.Is(err, btf.ErrNoExtendedInfo) {
210 return nil, fmt.Errorf("program %s: %w", funcSym.Name, err)
200211 }
201212 }
202213
214225 for _, prog := range progs {
215226 err := link(prog, libs)
216227 if err != nil {
217 return nil, xerrors.Errorf("program %s: %w", prog.Name, err)
228 return nil, fmt.Errorf("program %s: %w", prog.Name, err)
218229 }
219230 res[prog.Name] = prog
220231 }
235246 return insns, offset, nil
236247 }
237248 if err != nil {
238 return nil, 0, xerrors.Errorf("offset %d: %w", offset, err)
249 return nil, 0, fmt.Errorf("offset %d: %w", offset, err)
239250 }
240251
241252 ins.Symbol = symbols[offset].Name
242253
243254 if rel, ok := relocations[offset]; ok {
244255 if err = ec.relocateInstruction(&ins, rel); err != nil {
245 return nil, 0, xerrors.Errorf("offset %d: can't relocate instruction: %w", offset, err)
256 return nil, 0, fmt.Errorf("offset %d: can't relocate instruction: %w", offset, err)
246257 }
247258 }
248259
263274 // from the section itself.
264275 idx := int(rel.Section)
265276 if idx > len(ec.Sections) {
266 return xerrors.New("out-of-bounds section index")
277 return errors.New("out-of-bounds section index")
267278 }
268279
269280 name = ec.Sections[idx].Name
283294 // section. Weirdly, the offset of the real symbol in the
284295 // section is encoded in the instruction stream.
285296 if bind != elf.STB_LOCAL {
286 return xerrors.Errorf("direct load: %s: unsupported relocation %s", name, bind)
297 return fmt.Errorf("direct load: %s: unsupported relocation %s", name, bind)
287298 }
288299
289300 // For some reason, clang encodes the offset of the symbol its
305316
306317 case elf.STT_OBJECT:
307318 if bind != elf.STB_GLOBAL {
308 return xerrors.Errorf("load: %s: unsupported binding: %s", name, bind)
319 return fmt.Errorf("load: %s: unsupported binding: %s", name, bind)
309320 }
310321
311322 ins.Src = asm.PseudoMapFD
312323
313324 default:
314 return xerrors.Errorf("load: %s: unsupported relocation: %s", name, typ)
325 return fmt.Errorf("load: %s: unsupported relocation: %s", name, typ)
315326 }
316327
317328 // Mark the instruction as needing an update when creating the
322333
323334 case ins.OpCode.JumpOp() == asm.Call:
324335 if ins.Src != asm.PseudoCall {
325 return xerrors.Errorf("call: %s: incorrect source register", name)
336 return fmt.Errorf("call: %s: incorrect source register", name)
326337 }
327338
328339 switch typ {
329340 case elf.STT_NOTYPE, elf.STT_FUNC:
330341 if bind != elf.STB_GLOBAL {
331 return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind)
342 return fmt.Errorf("call: %s: unsupported binding: %s", name, bind)
332343 }
333344
334345 case elf.STT_SECTION:
335346 if bind != elf.STB_LOCAL {
336 return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind)
347 return fmt.Errorf("call: %s: unsupported binding: %s", name, bind)
337348 }
338349
339350 // The function we want to call is in the indicated section,
342353 // A value of -1 references the first instruction in the section.
343354 offset := int64(int32(ins.Constant)+1) * asm.InstructionSize
344355 if offset < 0 {
345 return xerrors.Errorf("call: %s: invalid offset %d", name, offset)
356 return fmt.Errorf("call: %s: invalid offset %d", name, offset)
346357 }
347358
348359 sym, ok := ec.symbolsPerSection[rel.Section][uint64(offset)]
349360 if !ok {
350 return xerrors.Errorf("call: %s: no symbol at offset %d", name, offset)
361 return fmt.Errorf("call: %s: no symbol at offset %d", name, offset)
351362 }
352363
353364 ins.Constant = -1
354365 name = sym.Name
355366
356367 default:
357 return xerrors.Errorf("call: %s: invalid symbol type %s", name, typ)
368 return fmt.Errorf("call: %s: invalid symbol type %s", name, typ)
358369 }
359370
360371 default:
361 return xerrors.Errorf("relocation for unsupported instruction: %s", ins.OpCode)
372 return fmt.Errorf("relocation for unsupported instruction: %s", ins.OpCode)
362373 }
363374
364375 ins.Reference = name
369380 for idx, sec := range mapSections {
370381 syms := ec.symbolsPerSection[idx]
371382 if len(syms) == 0 {
372 return xerrors.Errorf("section %v: no symbols", sec.Name)
383 return fmt.Errorf("section %v: no symbols", sec.Name)
373384 }
374385
375386 if sec.Size%uint64(len(syms)) != 0 {
376 return xerrors.Errorf("section %v: map descriptors are not of equal size", sec.Name)
387 return fmt.Errorf("section %v: map descriptors are not of equal size", sec.Name)
377388 }
378389
379390 var (
383394 for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size {
384395 mapSym, ok := syms[offset]
385396 if !ok {
386 return xerrors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
397 return fmt.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
387398 }
388399
389400 if maps[mapSym.Name] != nil {
390 return xerrors.Errorf("section %v: map %v already exists", sec.Name, mapSym)
401 return fmt.Errorf("section %v: map %v already exists", sec.Name, mapSym)
391402 }
392403
393404 lr := io.LimitReader(r, int64(size))
397408 }
398409 switch {
399410 case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
400 return xerrors.Errorf("map %v: missing type", mapSym)
411 return fmt.Errorf("map %v: missing type", mapSym)
401412 case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil:
402 return xerrors.Errorf("map %v: missing key size", mapSym)
413 return fmt.Errorf("map %v: missing key size", mapSym)
403414 case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil:
404 return xerrors.Errorf("map %v: missing value size", mapSym)
415 return fmt.Errorf("map %v: missing value size", mapSym)
405416 case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil:
406 return xerrors.Errorf("map %v: missing max entries", mapSym)
417 return fmt.Errorf("map %v: missing max entries", mapSym)
407418 case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil:
408 return xerrors.Errorf("map %v: missing flags", mapSym)
419 return fmt.Errorf("map %v: missing flags", mapSym)
409420 }
410421
411422 if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil {
412 return xerrors.Errorf("map %v: unknown and non-zero fields in definition", mapSym)
423 return fmt.Errorf("map %v: unknown and non-zero fields in definition", mapSym)
413424 }
414425
415426 maps[mapSym.Name] = &spec
421432
422433 func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error {
423434 if spec == nil {
424 return xerrors.Errorf("missing BTF")
435 return fmt.Errorf("missing BTF")
425436 }
426437
427438 for idx, sec := range mapSections {
428439 syms := ec.symbolsPerSection[idx]
429440 if len(syms) == 0 {
430 return xerrors.Errorf("section %v: no symbols", sec.Name)
441 return fmt.Errorf("section %v: no symbols", sec.Name)
431442 }
432443
433444 for _, sym := range syms {
434445 name := sym.Name
435446 if maps[name] != nil {
436 return xerrors.Errorf("section %v: map %v already exists", sec.Name, sym)
437 }
438
439 btfMap, btfMapMembers, err := spec.Map(name)
447 return fmt.Errorf("section %v: map %v already exists", sec.Name, sym)
448 }
449
450 mapSpec, err := mapSpecFromBTF(spec, name)
440451 if err != nil {
441 return xerrors.Errorf("map %v: can't get BTF: %w", name, err)
442 }
443
444 spec, err := mapSpecFromBTF(btfMap, btfMapMembers)
445 if err != nil {
446 return xerrors.Errorf("map %v: %w", name, err)
447 }
448
449 maps[name] = spec
452 return fmt.Errorf("map %v: %w", name, err)
453 }
454
455 maps[name] = mapSpec
450456 }
451457 }
452458
453459 return nil
454460 }
455461
456 func mapSpecFromBTF(btfMap *btf.Map, btfMapMembers []btf.Member) (*MapSpec, error) {
462 func mapSpecFromBTF(spec *btf.Spec, name string) (*MapSpec, error) {
463 btfMap, btfMapMembers, err := spec.Map(name)
464 if err != nil {
465 return nil, fmt.Errorf("can't get BTF: %w", err)
466 }
467
468 keyType := btf.MapKey(btfMap)
469 size, err := btf.Sizeof(keyType)
470 if err != nil {
471 return nil, fmt.Errorf("can't get size of BTF key: %w", err)
472 }
473 keySize := uint32(size)
474
475 valueType := btf.MapValue(btfMap)
476 size, err = btf.Sizeof(valueType)
477 if err != nil {
478 return nil, fmt.Errorf("can't get size of BTF value: %w", err)
479 }
480 valueSize := uint32(size)
481
457482 var (
458483 mapType, flags, maxEntries uint32
459 err error
460484 )
461485 for _, member := range btfMapMembers {
462486 switch member.Name {
463487 case "type":
464488 mapType, err = uintFromBTF(member.Type)
465489 if err != nil {
466 return nil, xerrors.Errorf("can't get type: %w", err)
490 return nil, fmt.Errorf("can't get type: %w", err)
467491 }
468492
469493 case "map_flags":
470494 flags, err = uintFromBTF(member.Type)
471495 if err != nil {
472 return nil, xerrors.Errorf("can't get BTF map flags: %w", err)
496 return nil, fmt.Errorf("can't get BTF map flags: %w", err)
473497 }
474498
475499 case "max_entries":
476500 maxEntries, err = uintFromBTF(member.Type)
477501 if err != nil {
478 return nil, xerrors.Errorf("can't get BTF map max entries: %w", err)
479 }
480
481 case "key":
482 case "value":
502 return nil, fmt.Errorf("can't get BTF map max entries: %w", err)
503 }
504
505 case "key_size":
506 if _, isVoid := keyType.(*btf.Void); !isVoid {
507 return nil, errors.New("both key and key_size given")
508 }
509
510 keySize, err = uintFromBTF(member.Type)
511 if err != nil {
512 return nil, fmt.Errorf("can't get BTF key size: %w", err)
513 }
514
515 case "value_size":
516 if _, isVoid := valueType.(*btf.Void); !isVoid {
517 return nil, errors.New("both value and value_size given")
518 }
519
520 valueSize, err = uintFromBTF(member.Type)
521 if err != nil {
522 return nil, fmt.Errorf("can't get BTF value size: %w", err)
523 }
524
525 case "pinning":
526 pinning, err := uintFromBTF(member.Type)
527 if err != nil {
528 return nil, fmt.Errorf("can't get pinning: %w", err)
529 }
530
531 if pinning != 0 {
532 return nil, fmt.Errorf("'pinning' attribute not supported: %w", ErrNotSupported)
533 }
534
535 case "key", "value":
483536 default:
484 return nil, xerrors.Errorf("unrecognized field %s in BTF map definition", member.Name)
485 }
486 }
487
488 keySize, err := btf.Sizeof(btf.MapKey(btfMap))
489 if err != nil {
490 return nil, xerrors.Errorf("can't get size of BTF key: %w", err)
491 }
492
493 valueSize, err := btf.Sizeof(btf.MapValue(btfMap))
494 if err != nil {
495 return nil, xerrors.Errorf("can't get size of BTF value: %w", err)
537 return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name)
538 }
496539 }
497540
498541 return &MapSpec{
499542 Type: MapType(mapType),
500 KeySize: uint32(keySize),
501 ValueSize: uint32(valueSize),
543 KeySize: keySize,
544 ValueSize: valueSize,
502545 MaxEntries: maxEntries,
503546 Flags: flags,
504547 BTF: btfMap,
510553 func uintFromBTF(typ btf.Type) (uint32, error) {
511554 ptr, ok := typ.(*btf.Pointer)
512555 if !ok {
513 return 0, xerrors.Errorf("not a pointer: %v", typ)
556 return 0, fmt.Errorf("not a pointer: %v", typ)
514557 }
515558
516559 arr, ok := ptr.Target.(*btf.Array)
517560 if !ok {
518 return 0, xerrors.Errorf("not a pointer to array: %v", typ)
561 return 0, fmt.Errorf("not a pointer to array: %v", typ)
519562 }
520563
521564 return arr.Nelems, nil
523566
524567 func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error {
525568 if spec == nil {
526 return xerrors.New("data sections require BTF")
569 return errors.New("data sections require BTF, make sure all consts are marked as static")
527570 }
528571
529572 for _, sec := range dataSections {
534577
535578 data, err := sec.Data()
536579 if err != nil {
537 return xerrors.Errorf("data section %s: can't get contents: %w", sec.Name, err)
580 return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err)
538581 }
539582
540583 if uint64(len(data)) > math.MaxUint32 {
541 return xerrors.Errorf("data section %s: contents exceed maximum size", sec.Name)
584 return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name)
542585 }
543586
544587 mapSpec := &MapSpec{
565608 return nil
566609 }
567610
568 func getProgType(v string) (ProgramType, AttachType) {
569 types := map[string]ProgramType{
570 // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c#n3568
571 "socket": SocketFilter,
572 "seccomp": SocketFilter,
573 "kprobe/": Kprobe,
574 "uprobe/": Kprobe,
575 "kretprobe/": Kprobe,
576 "uretprobe/": Kprobe,
577 "tracepoint/": TracePoint,
578 "raw_tracepoint/": RawTracepoint,
579 "xdp": XDP,
580 "perf_event": PerfEvent,
581 "lwt_in": LWTIn,
582 "lwt_out": LWTOut,
583 "lwt_xmit": LWTXmit,
584 "lwt_seg6local": LWTSeg6Local,
585 "sockops": SockOps,
586 "sk_skb": SkSKB,
587 "sk_msg": SkMsg,
588 "lirc_mode2": LircMode2,
589 "flow_dissector": FlowDissector,
590
591 "cgroup_skb/": CGroupSKB,
592 "cgroup/dev": CGroupDevice,
593 "cgroup/skb": CGroupSKB,
594 "cgroup/sock": CGroupSock,
595 "cgroup/post_bind": CGroupSock,
596 "cgroup/bind": CGroupSockAddr,
597 "cgroup/connect": CGroupSockAddr,
598 "cgroup/sendmsg": CGroupSockAddr,
599 "cgroup/recvmsg": CGroupSockAddr,
600 "cgroup/sysctl": CGroupSysctl,
601 "cgroup/getsockopt": CGroupSockopt,
602 "cgroup/setsockopt": CGroupSockopt,
603 "classifier": SchedCLS,
604 "action": SchedACT,
605 }
606 attachTypes := map[string]AttachType{
607 "cgroup_skb/ingress": AttachCGroupInetIngress,
608 "cgroup_skb/egress": AttachCGroupInetEgress,
609 "cgroup/sock": AttachCGroupInetSockCreate,
610 "cgroup/post_bind4": AttachCGroupInet4PostBind,
611 "cgroup/post_bind6": AttachCGroupInet6PostBind,
612 "cgroup/dev": AttachCGroupDevice,
613 "sockops": AttachCGroupSockOps,
614 "sk_skb/stream_parser": AttachSkSKBStreamParser,
615 "sk_skb/stream_verdict": AttachSkSKBStreamVerdict,
616 "sk_msg": AttachSkSKBStreamVerdict,
617 "lirc_mode2": AttachLircMode2,
618 "flow_dissector": AttachFlowDissector,
619 "cgroup/bind4": AttachCGroupInet4Bind,
620 "cgroup/bind6": AttachCGroupInet6Bind,
621 "cgroup/connect4": AttachCGroupInet4Connect,
622 "cgroup/connect6": AttachCGroupInet6Connect,
623 "cgroup/sendmsg4": AttachCGroupUDP4Sendmsg,
624 "cgroup/sendmsg6": AttachCGroupUDP6Sendmsg,
625 "cgroup/recvmsg4": AttachCGroupUDP4Recvmsg,
626 "cgroup/recvmsg6": AttachCGroupUDP6Recvmsg,
627 "cgroup/sysctl": AttachCGroupSysctl,
628 "cgroup/getsockopt": AttachCGroupGetsockopt,
629 "cgroup/setsockopt": AttachCGroupSetsockopt,
630 }
631 attachType := AttachNone
632 for k, t := range attachTypes {
633 if strings.HasPrefix(v, k) {
634 attachType = t
635 }
636 }
637
638 for k, t := range types {
639 if strings.HasPrefix(v, k) {
640 return t, attachType
641 }
642 }
643 return UnspecifiedProgram, AttachNone
644 }
645
646 func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (map[elf.SectionIndex]map[uint64]elf.Symbol, error) {
611 func getProgType(sectionName string) (ProgramType, AttachType, string) {
612 types := map[string]struct {
613 progType ProgramType
614 attachType AttachType
615 }{
616 // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c
617 "socket": {SocketFilter, AttachNone},
618 "seccomp": {SocketFilter, AttachNone},
619 "kprobe/": {Kprobe, AttachNone},
620 "uprobe/": {Kprobe, AttachNone},
621 "kretprobe/": {Kprobe, AttachNone},
622 "uretprobe/": {Kprobe, AttachNone},
623 "tracepoint/": {TracePoint, AttachNone},
624 "raw_tracepoint/": {RawTracepoint, AttachNone},
625 "xdp": {XDP, AttachNone},
626 "perf_event": {PerfEvent, AttachNone},
627 "lwt_in": {LWTIn, AttachNone},
628 "lwt_out": {LWTOut, AttachNone},
629 "lwt_xmit": {LWTXmit, AttachNone},
630 "lwt_seg6local": {LWTSeg6Local, AttachNone},
631 "sockops": {SockOps, AttachCGroupSockOps},
632 "sk_skb/stream_parser": {SkSKB, AttachSkSKBStreamParser},
633 "sk_skb/stream_verdict": {SkSKB, AttachSkSKBStreamParser},
634 "sk_msg": {SkMsg, AttachSkSKBStreamVerdict},
635 "lirc_mode2": {LircMode2, AttachLircMode2},
636 "flow_dissector": {FlowDissector, AttachFlowDissector},
637 "iter/": {Tracing, AttachTraceIter},
638 "sk_lookup/": {SkLookup, AttachSkLookup},
639
640 "cgroup_skb/ingress": {CGroupSKB, AttachCGroupInetIngress},
641 "cgroup_skb/egress": {CGroupSKB, AttachCGroupInetEgress},
642 "cgroup/dev": {CGroupDevice, AttachCGroupDevice},
643 "cgroup/skb": {CGroupSKB, AttachNone},
644 "cgroup/sock": {CGroupSock, AttachCGroupInetSockCreate},
645 "cgroup/post_bind4": {CGroupSock, AttachCGroupInet4PostBind},
646 "cgroup/post_bind6": {CGroupSock, AttachCGroupInet6PostBind},
647 "cgroup/bind4": {CGroupSockAddr, AttachCGroupInet4Bind},
648 "cgroup/bind6": {CGroupSockAddr, AttachCGroupInet6Bind},
649 "cgroup/connect4": {CGroupSockAddr, AttachCGroupInet4Connect},
650 "cgroup/connect6": {CGroupSockAddr, AttachCGroupInet6Connect},
651 "cgroup/sendmsg4": {CGroupSockAddr, AttachCGroupUDP4Sendmsg},
652 "cgroup/sendmsg6": {CGroupSockAddr, AttachCGroupUDP6Sendmsg},
653 "cgroup/recvmsg4": {CGroupSockAddr, AttachCGroupUDP4Recvmsg},
654 "cgroup/recvmsg6": {CGroupSockAddr, AttachCGroupUDP6Recvmsg},
655 "cgroup/sysctl": {CGroupSysctl, AttachCGroupSysctl},
656 "cgroup/getsockopt": {CGroupSockopt, AttachCGroupGetsockopt},
657 "cgroup/setsockopt": {CGroupSockopt, AttachCGroupSetsockopt},
658 "classifier": {SchedCLS, AttachNone},
659 "action": {SchedACT, AttachNone},
660 }
661
662 for prefix, t := range types {
663 if !strings.HasPrefix(sectionName, prefix) {
664 continue
665 }
666
667 if !strings.HasSuffix(prefix, "/") {
668 return t.progType, t.attachType, ""
669 }
670
671 return t.progType, t.attachType, sectionName[len(prefix):]
672 }
673
674 return UnspecifiedProgram, AttachNone, ""
675 }
676
677 func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (map[elf.SectionIndex]map[uint64]elf.Symbol, map[elf.SectionIndex]bool, error) {
647678 result := make(map[elf.SectionIndex]map[uint64]elf.Symbol)
679 targets := make(map[elf.SectionIndex]bool)
648680 for idx, sec := range sections {
649681 rels := make(map[uint64]elf.Symbol)
650682
651683 if sec.Entsize < 16 {
652 return nil, xerrors.Errorf("section %s: relocations are less than 16 bytes", sec.Name)
684 return nil, nil, fmt.Errorf("section %s: relocations are less than 16 bytes", sec.Name)
653685 }
654686
655687 r := sec.Open()
658690
659691 var rel elf.Rel64
660692 if binary.Read(ent, ec.ByteOrder, &rel) != nil {
661 return nil, xerrors.Errorf("can't parse relocation at offset %v", off)
693 return nil, nil, fmt.Errorf("can't parse relocation at offset %v", off)
662694 }
663695
664696 symNo := int(elf.R_SYM64(rel.Info) - 1)
665697 if symNo >= len(ec.symbols) {
666 return nil, xerrors.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo)
667 }
668
698 return nil, nil, fmt.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo)
699 }
700
701 symbol := ec.symbols[symNo]
702 targets[symbol.Section] = true
669703 rels[rel.Off] = ec.symbols[symNo]
670704 }
671705
672706 result[idx] = rels
673707 }
674 return result, nil
708 return result, targets, nil
675709 }
676710
677711 func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]elf.Symbol {
55 "reflect"
66 "testing"
77
8 "github.com/cilium/ebpf/internal"
89 "github.com/cilium/ebpf/internal/testutils"
910 )
1011
1112 func TestLoadCollectionSpec(t *testing.T) {
12 files, err := filepath.Glob("testdata/loader-*.elf")
13 if err != nil {
14 t.Fatal(err)
15 }
16
17 for _, file := range files {
18 name := filepath.Base(file)
19 t.Run(name, func(t *testing.T) {
20 spec, err := LoadCollectionSpec(file)
13 testutils.TestFiles(t, "testdata/loader-*.elf", func(t *testing.T, file string) {
14 spec, err := LoadCollectionSpec(file)
15 if err != nil {
16 t.Fatal("Can't parse ELF:", err)
17 }
18
19 hashMapSpec := &MapSpec{
20 Name: "hash_map",
21 Type: Hash,
22 KeySize: 4,
23 ValueSize: 2,
24 MaxEntries: 1,
25 }
26 checkMapSpec(t, spec.Maps, "hash_map", hashMapSpec)
27 checkMapSpec(t, spec.Maps, "array_of_hash_map", &MapSpec{
28 Name: "hash_map",
29 Type: ArrayOfMaps,
30 KeySize: 4,
31 MaxEntries: 2,
32 })
33 spec.Maps["array_of_hash_map"].InnerMap = spec.Maps["hash_map"]
34
35 hashMap2Spec := &MapSpec{
36 Name: "",
37 Type: Hash,
38 KeySize: 4,
39 ValueSize: 1,
40 MaxEntries: 2,
41 Flags: 1,
42 }
43 checkMapSpec(t, spec.Maps, "hash_map2", hashMap2Spec)
44 checkMapSpec(t, spec.Maps, "hash_of_hash_map", &MapSpec{
45 Type: HashOfMaps,
46 KeySize: 4,
47 MaxEntries: 2,
48 })
49 spec.Maps["hash_of_hash_map"].InnerMap = spec.Maps["hash_map2"]
50
51 checkProgramSpec(t, spec.Programs, "xdp_prog", &ProgramSpec{
52 Type: XDP,
53 License: "MIT",
54 KernelVersion: 0,
55 })
56 checkProgramSpec(t, spec.Programs, "no_relocation", &ProgramSpec{
57 Type: SocketFilter,
58 License: "MIT",
59 KernelVersion: 0,
60 })
61
62 if rodata := spec.Maps[".rodata"]; rodata != nil {
63 err := spec.RewriteConstants(map[string]interface{}{
64 "arg": uint32(1),
65 })
2166 if err != nil {
22 t.Fatal("Can't parse ELF:", err)
67 t.Fatal("Can't rewrite constant:", err)
2368 }
2469
25 hashMapSpec := &MapSpec{
26 Name: "hash_map",
27 Type: Hash,
28 KeySize: 4,
29 ValueSize: 2,
30 MaxEntries: 1,
70 err = spec.RewriteConstants(map[string]interface{}{
71 "totallyBogus": uint32(1),
72 })
73 if err == nil {
74 t.Error("Rewriting a bogus constant doesn't fail")
3175 }
32 checkMapSpec(t, spec.Maps, "hash_map", hashMapSpec)
33 checkMapSpec(t, spec.Maps, "array_of_hash_map", &MapSpec{
34 Name: "hash_map",
35 Type: ArrayOfMaps,
36 KeySize: 4,
37 MaxEntries: 2,
38 })
39 spec.Maps["array_of_hash_map"].InnerMap = spec.Maps["hash_map"]
40
41 hashMap2Spec := &MapSpec{
42 Name: "",
43 Type: Hash,
44 KeySize: 4,
45 ValueSize: 1,
46 MaxEntries: 2,
47 Flags: 1,
48 }
49 checkMapSpec(t, spec.Maps, "hash_map2", hashMap2Spec)
50 checkMapSpec(t, spec.Maps, "hash_of_hash_map", &MapSpec{
51 Type: HashOfMaps,
52 KeySize: 4,
53 MaxEntries: 2,
54 })
55 spec.Maps["hash_of_hash_map"].InnerMap = spec.Maps["hash_map2"]
56
57 checkProgramSpec(t, spec.Programs, "xdp_prog", &ProgramSpec{
58 Type: XDP,
59 License: "MIT",
60 KernelVersion: 0,
61 })
62 checkProgramSpec(t, spec.Programs, "no_relocation", &ProgramSpec{
63 Type: SocketFilter,
64 License: "MIT",
65 KernelVersion: 0,
66 })
67
68 if rodata := spec.Maps[".rodata"]; rodata != nil {
69 err := spec.RewriteConstants(map[string]interface{}{
70 "arg": uint32(1),
71 })
72 if err != nil {
73 t.Fatal("Can't rewrite constant:", err)
74 }
75
76 err = spec.RewriteConstants(map[string]interface{}{
77 "totallyBogus": uint32(1),
78 })
79 if err == nil {
80 t.Error("Rewriting a bogus constant doesn't fail")
81 }
82 }
83
84 t.Log(spec.Programs["xdp_prog"].Instructions)
85
86 coll, err := NewCollectionWithOptions(spec, CollectionOptions{
87 Programs: ProgramOptions{
88 LogLevel: 1,
89 },
90 })
91 testutils.SkipIfNotSupported(t, err)
92 if err != nil {
93 t.Fatal(err)
94 }
95 defer coll.Close()
96
97 ret, _, err := coll.Programs["xdp_prog"].Test(make([]byte, 14))
98 if err != nil {
99 t.Fatal("Can't run program:", err)
100 }
101
102 if ret != 5 {
103 t.Error("Expected return value to be 5, got", ret)
104 }
105 })
106 }
76 }
77
78 t.Log(spec.Programs["xdp_prog"].Instructions)
79
80 if spec.Programs["xdp_prog"].ByteOrder != internal.NativeEndian {
81 return
82 }
83
84 coll, err := NewCollectionWithOptions(spec, CollectionOptions{
85 Programs: ProgramOptions{
86 LogLevel: 1,
87 },
88 })
89 testutils.SkipIfNotSupported(t, err)
90 if err != nil {
91 t.Fatal(err)
92 }
93 defer coll.Close()
94
95 ret, _, err := coll.Programs["xdp_prog"].Test(make([]byte, 14))
96 if err != nil {
97 t.Fatal("Can't run program:", err)
98 }
99
100 if ret != 5 {
101 t.Error("Expected return value to be 5, got", ret)
102 }
103 })
107104 }
108105
109106 func checkMapSpec(t *testing.T, maps map[string]*MapSpec, name string, want *MapSpec) {
158155 if !ok {
159156 t.Fatalf("Missing program %s", name)
160157 return
158 }
159
160 if have.ByteOrder == nil {
161 t.Errorf("%s: nil ByteOrder", name)
161162 }
162163
163164 if have.License != want.License {
207208 }
208209
209210 func TestLoadInvalidMap(t *testing.T) {
210 _, err := LoadCollectionSpec("testdata/invalid_map.elf")
211 t.Log(err)
212 if err == nil {
213 t.Fatal("should be fail")
214 }
215 }
216
217 var elfPattern = flag.String("elfs", "", "`PATTERN` for a path containing libbpf-compatible ELFs")
211 testutils.TestFiles(t, "testdata/invalid_map-*.elf", func(t *testing.T, file string) {
212 _, err := LoadCollectionSpec(file)
213 t.Log(err)
214 if err == nil {
215 t.Fatal("Loading an invalid map should fail")
216 }
217 })
218 }
219
220 func TestLoadRawTracepoint(t *testing.T) {
221 testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API")
222
223 testutils.TestFiles(t, "testdata/raw_tracepoint-*.elf", func(t *testing.T, file string) {
224 spec, err := LoadCollectionSpec(file)
225 if err != nil {
226 t.Fatal("Can't parse ELF:", err)
227 }
228
229 if spec.Programs["sched_process_exec"].ByteOrder != internal.NativeEndian {
230 return
231 }
232
233 coll, err := NewCollectionWithOptions(spec, CollectionOptions{
234 Programs: ProgramOptions{
235 LogLevel: 1,
236 },
237 })
238 testutils.SkipIfNotSupported(t, err)
239 if err != nil {
240 t.Fatal("Can't create collection:", err)
241 }
242
243 coll.Close()
244 })
245 }
246
247 var (
248 elfPath = flag.String("elfs", "", "`Path` containing libbpf-compatible ELFs")
249 elfPattern = flag.String("elf-pattern", "*.o", "Glob `pattern` for object files that should be tested")
250 )
218251
219252 func TestLibBPFCompat(t *testing.T) {
220 if *elfPattern == "" {
253 if *elfPath == "" {
221254 // Specify the path to the directory containing the eBPF for
222 // the kernel's selftests.
223 // As of 5.2 that is tools/testing/selftests/bpf/.
255 // the kernel's selftests if you want to run this test.
256 // As of 5.2 that is tools/testing/selftests/bpf/
224257 t.Skip("No path specified")
225258 }
226259
227 files, err := filepath.Glob(*elfPattern)
228 if err != nil {
229 t.Fatal(err)
230 }
231
232 for _, file := range files {
233 name := filepath.Base(file)
234 t.Run(name, func(t *testing.T) {
235 t.Parallel()
236
237 spec, err := LoadCollectionSpec(file)
238 if err != nil {
239 t.Fatalf("Can't read %s: %s", name, err)
240 }
241
242 coll, err := NewCollection(spec)
243 if err != nil {
244 t.Fatal(err)
245 }
246 coll.Close()
247 })
248 }
249 }
260 testutils.TestFiles(t, filepath.Join(*elfPath, *elfPattern), func(t *testing.T, path string) {
261 t.Parallel()
262
263 file := filepath.Base(path)
264 _, err := LoadCollectionSpec(path)
265 testutils.SkipIfNotSupported(t, err)
266 if err != nil {
267 t.Fatalf("Can't read %s: %s", file, err)
268 }
269 })
270 }
271
272 func TestGetProgType(t *testing.T) {
273 testcases := []struct {
274 section string
275 pt ProgramType
276 at AttachType
277 to string
278 }{
279 {"socket/garbage", SocketFilter, AttachNone, ""},
280 {"kprobe/func", Kprobe, AttachNone, "func"},
281 {"xdp/foo", XDP, AttachNone, ""},
282 {"cgroup_skb/ingress", CGroupSKB, AttachCGroupInetIngress, ""},
283 {"iter/bpf_map", Tracing, AttachTraceIter, "bpf_map"},
284 }
285
286 for _, tc := range testcases {
287 pt, at, to := getProgType(tc.section)
288 if pt != tc.pt {
289 t.Errorf("section %s: expected type %s, got %s", tc.section, tc.pt, pt)
290 }
291
292 if at != tc.at {
293 t.Errorf("section %s: expected attach type %s, got %s", tc.section, tc.at, at)
294 }
295
296 if to != tc.to {
297 t.Errorf("section %s: expected attachment to be %q, got %q", tc.section, tc.to, to)
298 }
299 }
300 }
44 import (
55 "context"
66 "fmt"
7 "io/ioutil"
78 "os"
9 "strconv"
10 "strings"
811 "time"
912
1013 "github.com/cilium/ebpf"
1417 "golang.org/x/sys/unix"
1518 )
1619
17 // Example_program demonstrates how to attach an eBPF program to a tracepoint.
20 // getTracepointID returns the system specific ID for the tracepoint sys_enter_open.
21 func getTracepointID() (uint64, error) {
22 data, err := ioutil.ReadFile("/sys/kernel/debug/tracing/events/syscalls/sys_enter_open/id")
23 if err != nil {
24 return 0, fmt.Errorf("failed to read tracepoint ID for 'sys_enter_open': %v", err)
25 }
26 tid := strings.TrimSuffix(string(data), "\n")
27 return strconv.ParseUint(tid, 10, 64)
28 }
29
30 // This demonstrates how to attach an eBPF program to a tracepoint.
1831 // The program will be attached to the sys_enter_open syscall and print out the integer
1932 // 123 everytime the sycall is used.
20 func Example_program() {
33 func Example_tracepoint() {
2134 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
2235 defer cancel()
2336 events, err := ebpf.NewMap(&ebpf.MapSpec{
8497 }
8598 defer prog.Close()
8699
87 // tracepoint id from /sys/kernel/debug/tracing/events/syscalls/sys_enter_open/id
88 tid := uint64(627)
100 tid, err := getTracepointID()
101 if err != nil {
102 panic(fmt.Errorf("could not get tracepoint id: %v", err))
103 }
89104
90105 attr := unix.PerfEventAttr{
91106 Type: unix.PERF_TYPE_TRACEPOINT,
99114 panic(fmt.Errorf("unable to open perf events: %v", err))
100115 }
101116 if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(pfd), unix.PERF_EVENT_IOC_ENABLE, 0); errno != 0 {
102 panic(fmt.Errorf("unable to set up perf events: %v", err))
117 panic(fmt.Errorf("unable to enable perf events: %v", err))
103118 }
104119 if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(pfd), unix.PERF_EVENT_IOC_SET_BPF, uintptr(prog.FD())); errno != 0 {
105120 panic(fmt.Errorf("unable to attach bpf program to perf events: %v", err))
106121 }
107122
108123 <-ctx.Done()
124
125 if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(pfd), unix.PERF_EVENT_IOC_DISABLE, 0); errno != 0 {
126 panic(fmt.Errorf("unable to disable perf events: %v", err))
127 }
109128 }
8989 panic(err)
9090 }
9191
92 coll, err := ebpf.NewCollection(spec)
93 if err != nil {
92 var objs struct {
93 Prog *ebpf.Program `ebpf:"bpf_prog1"`
94 Stats *ebpf.Map `ebpf:"my_map"`
95 }
96
97 if err := spec.LoadAndAssign(&objs, nil); err != nil {
9498 panic(err)
9599 }
96 defer coll.Close()
100 defer objs.Prog.Close()
101 defer objs.Stats.Close()
97102
98103 sock, err := openRawSock(*index)
99104 if err != nil {
101106 }
102107 defer syscall.Close(sock)
103108
104 prog := coll.DetachProgram("bpf_prog1")
105 if prog == nil {
106 panic("no program named bpf_prog1 found")
107 }
108 defer prog.Close()
109
110 if err := syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, SO_ATTACH_BPF, prog.FD()); err != nil {
109 if err := syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, SO_ATTACH_BPF, objs.Prog.FD()); err != nil {
111110 panic(err)
112111 }
113112
114113 fmt.Printf("Filtering on eth index: %d\n", *index)
115114 fmt.Println("Packet stats:")
116
117 protoStats := coll.DetachMap("my_map")
118 if protoStats == nil {
119 panic(fmt.Errorf("no map named my_map found"))
120 }
121 defer protoStats.Close()
122115
123116 for {
124117 const (
131124 var icmp uint64
132125 var tcp uint64
133126 var udp uint64
134 err := protoStats.Lookup(uint32(ICMP), &icmp)
127 err := objs.Stats.Lookup(uint32(ICMP), &icmp)
135128 if err != nil {
136129 panic(err)
137130 }
138 err = protoStats.Lookup(uint32(TCP), &tcp)
131 err = objs.Stats.Lookup(uint32(TCP), &tcp)
139132 if err != nil {
140133 panic(err)
141134 }
142 err = protoStats.Lookup(uint32(UDP), &udp)
135 err = objs.Stats.Lookup(uint32(UDP), &udp)
143136 if err != nil {
144137 panic(err)
145138 }
00 module github.com/cilium/ebpf
11
2 go 1.12
2 go 1.13
33
4 require (
5 golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
6 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
7 )
4 require golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
0 golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
1 golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
20 golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
31 golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
5 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
33 "bytes"
44 "debug/elf"
55 "encoding/binary"
6 "errors"
7 "fmt"
68 "io"
79 "io/ioutil"
810 "math"
11 "os"
912 "reflect"
13 "sync"
1014 "unsafe"
1115
1216 "github.com/cilium/ebpf/internal"
1317 "github.com/cilium/ebpf/internal/unix"
14
15 "golang.org/x/xerrors"
1618 )
1719
1820 const btfMagic = 0xeB9F
1921
2022 // Errors returned by BTF functions.
2123 var (
22 ErrNotSupported = internal.ErrNotSupported
24 ErrNotSupported = internal.ErrNotSupported
25 ErrNotFound = errors.New("not found")
26 ErrNoExtendedInfo = errors.New("no extended info")
2327 )
2428
2529 // Spec represents decoded BTF.
2933 types map[string][]Type
3034 funcInfos map[string]extInfo
3135 lineInfos map[string]extInfo
36 byteOrder binary.ByteOrder
3237 }
3338
3439 type btfHeader struct {
7176 }
7277
7378 if sec.Size > math.MaxUint32 {
74 return nil, xerrors.Errorf("section %s exceeds maximum size", sec.Name)
79 return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name)
7580 }
7681
7782 sectionSizes[sec.Name] = uint32(sec.Size)
8489
8590 symbols, err := file.Symbols()
8691 if err != nil {
87 return nil, xerrors.Errorf("can't read symbols: %v", err)
92 return nil, fmt.Errorf("can't read symbols: %v", err)
8893 }
8994
9095 variableOffsets := make(map[variable]uint32)
100105 }
101106
102107 if symbol.Value > math.MaxUint32 {
103 return nil, xerrors.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name)
108 return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name)
104109 }
105110
106111 variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value)
107112 }
108113
109 rawTypes, rawStrings, err := parseBTF(btfSection.Open(), file.ByteOrder)
114 spec, err := loadNakedSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets)
110115 if err != nil {
111116 return nil, err
112117 }
113118
119 if btfExtSection == nil {
120 return spec, nil
121 }
122
123 spec.funcInfos, spec.lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings)
124 if err != nil {
125 return nil, fmt.Errorf("can't read ext info: %w", err)
126 }
127
128 return spec, nil
129 }
130
131 func loadNakedSpec(btf io.ReadSeeker, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) {
132 rawTypes, rawStrings, err := parseBTF(btf, bo)
133 if err != nil {
134 return nil, err
135 }
136
114137 err = fixupDatasec(rawTypes, rawStrings, sectionSizes, variableOffsets)
115138 if err != nil {
116139 return nil, err
119142 types, err := inflateRawTypes(rawTypes, rawStrings)
120143 if err != nil {
121144 return nil, err
122 }
123
124 var (
125 funcInfos = make(map[string]extInfo)
126 lineInfos = make(map[string]extInfo)
127 )
128 if btfExtSection != nil {
129 funcInfos, lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, rawStrings)
130 if err != nil {
131 return nil, xerrors.Errorf("can't read ext info: %w", err)
132 }
133145 }
134146
135147 return &Spec{
136148 rawTypes: rawTypes,
137149 types: types,
138150 strings: rawStrings,
139 funcInfos: funcInfos,
140 lineInfos: lineInfos,
151 byteOrder: bo,
141152 }, nil
153 }
154
155 var kernelBTF struct {
156 sync.Mutex
157 *Spec
158 }
159
160 // LoadKernelSpec returns the current kernel's BTF information.
161 //
162 // Requires a >= 5.5 kernel with CONFIG_DEBUG_INFO_BTF enabled. Returns
163 // ErrNotSupported if BTF is not enabled.
164 func LoadKernelSpec() (*Spec, error) {
165 kernelBTF.Lock()
166 defer kernelBTF.Unlock()
167
168 if kernelBTF.Spec != nil {
169 return kernelBTF.Spec, nil
170 }
171
172 var err error
173 kernelBTF.Spec, err = loadKernelSpec()
174 return kernelBTF.Spec, err
175 }
176
177 func loadKernelSpec() (*Spec, error) {
178 fh, err := os.Open("/sys/kernel/btf/vmlinux")
179 if os.IsNotExist(err) {
180 return nil, fmt.Errorf("can't open kernel BTF at /sys/kernel/btf/vmlinux: %w", ErrNotFound)
181 }
182 if err != nil {
183 return nil, fmt.Errorf("can't read kernel BTF: %s", err)
184 }
185 defer fh.Close()
186
187 return loadNakedSpec(fh, internal.NativeEndian, nil, nil)
142188 }
143189
144190 func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) {
145191 rawBTF, err := ioutil.ReadAll(btf)
146192 if err != nil {
147 return nil, nil, xerrors.Errorf("can't read BTF: %v", err)
193 return nil, nil, fmt.Errorf("can't read BTF: %v", err)
148194 }
149195
150196 rd := bytes.NewReader(rawBTF)
151197
152198 var header btfHeader
153199 if err := binary.Read(rd, bo, &header); err != nil {
154 return nil, nil, xerrors.Errorf("can't read header: %v", err)
200 return nil, nil, fmt.Errorf("can't read header: %v", err)
155201 }
156202
157203 if header.Magic != btfMagic {
158 return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic)
204 return nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic)
159205 }
160206
161207 if header.Version != 1 {
162 return nil, nil, xerrors.Errorf("unexpected version %v", header.Version)
208 return nil, nil, fmt.Errorf("unexpected version %v", header.Version)
163209 }
164210
165211 if header.Flags != 0 {
166 return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags)
212 return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags)
167213 }
168214
169215 remainder := int64(header.HdrLen) - int64(binary.Size(&header))
170216 if remainder < 0 {
171 return nil, nil, xerrors.New("header is too short")
217 return nil, nil, errors.New("header is too short")
172218 }
173219
174220 if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil {
175 return nil, nil, xerrors.Errorf("header padding: %v", err)
221 return nil, nil, fmt.Errorf("header padding: %v", err)
176222 }
177223
178224 if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil {
179 return nil, nil, xerrors.Errorf("can't seek to start of string section: %v", err)
225 return nil, nil, fmt.Errorf("can't seek to start of string section: %v", err)
180226 }
181227
182228 rawStrings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen)))
183229 if err != nil {
184 return nil, nil, xerrors.Errorf("can't read type names: %w", err)
230 return nil, nil, fmt.Errorf("can't read type names: %w", err)
185231 }
186232
187233 if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil {
188 return nil, nil, xerrors.Errorf("can't seek to start of type section: %v", err)
234 return nil, nil, fmt.Errorf("can't seek to start of type section: %v", err)
189235 }
190236
191237 rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo)
192238 if err != nil {
193 return nil, nil, xerrors.Errorf("can't read types: %w", err)
239 return nil, nil, fmt.Errorf("can't read types: %w", err)
194240 }
195241
196242 return rawTypes, rawStrings, nil
212258 return err
213259 }
214260
261 if name == ".kconfig" || name == ".ksym" {
262 return fmt.Errorf("reference to %s: %w", name, ErrNotSupported)
263 }
264
215265 size, ok := sectionSizes[name]
216266 if !ok {
217 return xerrors.Errorf("data section %s: missing size", name)
267 return fmt.Errorf("data section %s: missing size", name)
218268 }
219269
220270 rawTypes[i].SizeType = size
223273 for j, secInfo := range secinfos {
224274 id := int(secInfo.Type - 1)
225275 if id >= len(rawTypes) {
226 return xerrors.Errorf("data section %s: invalid type id %d for variable %d", name, id, j)
276 return fmt.Errorf("data section %s: invalid type id %d for variable %d", name, id, j)
227277 }
228278
229279 varName, err := rawStrings.Lookup(rawTypes[id].NameOff)
230280 if err != nil {
231 return xerrors.Errorf("data section %s: can't get name for type %d: %w", name, id, err)
281 return fmt.Errorf("data section %s: can't get name for type %d: %w", name, id, err)
232282 }
233283
234284 offset, ok := variableOffsets[variable{name, varName}]
235285 if !ok {
236 return xerrors.Errorf("data section %s: missing offset for variable %s", name, varName)
286 return fmt.Errorf("data section %s: missing offset for variable %s", name, varName)
237287 }
238288
239289 secinfos[j].Offset = offset
243293 return nil
244294 }
245295
246 func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) {
296 type marshalOpts struct {
297 ByteOrder binary.ByteOrder
298 StripFuncLinkage bool
299 }
300
301 func (s *Spec) marshal(opts marshalOpts) ([]byte, error) {
247302 var (
248303 buf bytes.Buffer
249304 header = new(btfHeader)
255310 _, _ = buf.Write(make([]byte, headerLen))
256311
257312 // Write type section, just after the header.
258 for _, typ := range s.rawTypes {
259 if err := typ.Marshal(&buf, bo); err != nil {
260 return nil, xerrors.Errorf("can't marshal BTF: %w", err)
313 for _, raw := range s.rawTypes {
314 switch {
315 case opts.StripFuncLinkage && raw.Kind() == kindFunc:
316 raw.SetLinkage(linkageStatic)
317 }
318
319 if err := raw.Marshal(&buf, opts.ByteOrder); err != nil {
320 return nil, fmt.Errorf("can't marshal BTF: %w", err)
261321 }
262322 }
263323
279339 }
280340
281341 raw := buf.Bytes()
282 err := binary.Write(sliceWriter(raw[:headerLen]), bo, header)
283 if err != nil {
284 return nil, xerrors.Errorf("can't write header: %v", err)
342 err := binary.Write(sliceWriter(raw[:headerLen]), opts.ByteOrder, header)
343 if err != nil {
344 return nil, fmt.Errorf("can't write header: %v", err)
285345 }
286346
287347 return raw, nil
291351
292352 func (sw sliceWriter) Write(p []byte) (int, error) {
293353 if len(p) != len(sw) {
294 return 0, xerrors.New("size doesn't match")
354 return 0, errors.New("size doesn't match")
295355 }
296356
297357 return copy(sw, p), nil
301361 //
302362 // Length is the number of bytes in the raw BPF instruction stream.
303363 //
304 // Returns an error if there is no BTF.
364 // Returns an error which may wrap ErrNoExtendedInfo if the Spec doesn't
365 // contain extended BTF info.
305366 func (s *Spec) Program(name string, length uint64) (*Program, error) {
306367 if length == 0 {
307 return nil, xerrors.New("length musn't be zero")
368 return nil, errors.New("length musn't be zero")
369 }
370
371 if s.funcInfos == nil && s.lineInfos == nil {
372 return nil, fmt.Errorf("BTF for section %s: %w", name, ErrNoExtendedInfo)
308373 }
309374
310375 funcInfos, funcOK := s.funcInfos[name]
311376 lineInfos, lineOK := s.lineInfos[name]
312377
313378 if !funcOK && !lineOK {
314 return nil, xerrors.Errorf("no BTF for program %s", name)
379 return nil, fmt.Errorf("no extended BTF info for section %s", name)
315380 }
316381
317382 return &Program{s, length, funcInfos, lineInfos}, nil
328393
329394 mapStruct, ok := mapVar.Type.(*Struct)
330395 if !ok {
331 return nil, nil, xerrors.Errorf("expected struct, have %s", mapVar.Type)
396 return nil, nil, fmt.Errorf("expected struct, have %s", mapVar.Type)
332397 }
333398
334399 var key, value Type
343408 }
344409
345410 if key == nil {
346 return nil, nil, xerrors.Errorf("map %s: missing 'key' in type", name)
411 key = (*Void)(nil)
347412 }
348413
349414 if value == nil {
350 return nil, nil, xerrors.Errorf("map %s: missing 'value' in type", name)
415 value = (*Void)(nil)
351416 }
352417
353418 return &Map{s, key, value}, mapStruct.Members, nil
357422 func (s *Spec) Datasec(name string) (*Map, error) {
358423 var datasec Datasec
359424 if err := s.FindType(name, &datasec); err != nil {
360 return nil, xerrors.Errorf("data section %s: can't get BTF: %w", name, err)
425 return nil, fmt.Errorf("data section %s: can't get BTF: %w", name, err)
361426 }
362427
363428 return &Map{s, &Void{}, &datasec}, nil
364429 }
365430
366 var errNotFound = xerrors.New("not found")
367
368431 // FindType searches for a type with a specific name.
369432 //
370433 // hint determines the type of the returned Type.
371434 //
372 // Returns an error if there is no or multiple matches.
435 // Returns an error wrapping ErrNotFound if no matching
436 // type exists in spec.
373437 func (s *Spec) FindType(name string, typ Type) error {
374438 var (
375439 wanted = reflect.TypeOf(typ)
382446 }
383447
384448 if candidate != nil {
385 return xerrors.Errorf("type %s: multiple candidates for %T", name, typ)
449 return fmt.Errorf("type %s: multiple candidates for %T", name, typ)
386450 }
387451
388452 candidate = typ
389453 }
390454
391455 if candidate == nil {
392 return xerrors.Errorf("type %s: %w", name, errNotFound)
456 return fmt.Errorf("type %s: %w", name, ErrNotFound)
393457 }
394458
395459 value := reflect.Indirect(reflect.ValueOf(copyType(candidate)))
410474 return nil, err
411475 }
412476
413 btf, err := spec.marshal(internal.NativeEndian)
414 if err != nil {
415 return nil, xerrors.Errorf("can't marshal BTF: %w", err)
477 if spec.byteOrder != internal.NativeEndian {
478 return nil, fmt.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian)
479 }
480
481 btf, err := spec.marshal(marshalOpts{
482 ByteOrder: internal.NativeEndian,
483 StripFuncLinkage: haveFuncLinkage() != nil,
484 })
485 if err != nil {
486 return nil, fmt.Errorf("can't marshal BTF: %w", err)
416487 }
417488
418489 if uint64(len(btf)) > math.MaxUint32 {
419 return nil, xerrors.New("BTF exceeds the maximum size")
490 return nil, errors.New("BTF exceeds the maximum size")
420491 }
421492
422493 attr := &bpfLoadBTFAttr{
500571 func ProgramAppend(s, other *Program) error {
501572 funcInfos, err := s.funcInfos.append(other.funcInfos, s.length)
502573 if err != nil {
503 return xerrors.Errorf("func infos: %w", err)
574 return fmt.Errorf("func infos: %w", err)
504575 }
505576
506577 lineInfos, err := s.lineInfos.append(other.lineInfos, s.length)
507578 if err != nil {
508 return xerrors.Errorf("line infos: %w", err)
579 return fmt.Errorf("line infos: %w", err)
509580 }
510581
511582 s.length += other.length
559630 return internal.NewFD(uint32(fd)), nil
560631 }
561632
562 func minimalBTF(bo binary.ByteOrder) []byte {
633 func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte {
563634 const minHeaderLength = 24
564635
636 typesLen := uint32(binary.Size(types))
637 header := btfHeader{
638 Magic: btfMagic,
639 Version: 1,
640 HdrLen: minHeaderLength,
641 TypeOff: 0,
642 TypeLen: typesLen,
643 StringOff: typesLen,
644 StringLen: uint32(len(strings)),
645 }
646
647 buf := new(bytes.Buffer)
648 _ = binary.Write(buf, bo, &header)
649 _ = binary.Write(buf, bo, types)
650 buf.Write(strings)
651
652 return buf.Bytes()
653 }
654
655 var haveBTF = internal.FeatureTest("BTF", "5.1", func() (bool, error) {
565656 var (
566657 types struct {
567658 Integer btfType
568659 Var btfType
569660 btfVar struct{ Linkage uint32 }
570661 }
571 typLen = uint32(binary.Size(&types))
572662 strings = []byte{0, 'a', 0}
573 header = btfHeader{
574 Magic: btfMagic,
575 Version: 1,
576 HdrLen: minHeaderLength,
577 TypeOff: 0,
578 TypeLen: typLen,
579 StringOff: typLen,
580 StringLen: uint32(len(strings)),
581 }
582663 )
583664
584665 // We use a BTF_KIND_VAR here, to make sure that
589670 types.Var.SetKind(kindVar)
590671 types.Var.SizeType = 1
591672
592 buf := new(bytes.Buffer)
593 _ = binary.Write(buf, bo, &header)
594 _ = binary.Write(buf, bo, &types)
595 buf.Write(strings)
596
597 return buf.Bytes()
598 }
599
600 var haveBTF = internal.FeatureTest("BTF", "5.1", func() bool {
601 btf := minimalBTF(internal.NativeEndian)
673 btf := marshalBTF(&types, strings, internal.NativeEndian)
674
602675 fd, err := bpfLoadBTF(&bpfLoadBTFAttr{
603676 btf: internal.NewSlicePointer(btf),
604677 btfSize: uint32(len(btf)),
608681 }
609682 // Check for EINVAL specifically, rather than err != nil since we
610683 // otherwise misdetect due to insufficient permissions.
611 return !xerrors.Is(err, unix.EINVAL)
684 return !errors.Is(err, unix.EINVAL), nil
612685 })
686
687 var haveFuncLinkage = internal.FeatureTest("BTF func linkage", "5.6", func() (bool, error) {
688 var (
689 types struct {
690 FuncProto btfType
691 Func btfType
692 }
693 strings = []byte{0, 'a', 0}
694 )
695
696 types.FuncProto.SetKind(kindFuncProto)
697 types.Func.SetKind(kindFunc)
698 types.Func.SizeType = 1 // aka FuncProto
699 types.Func.NameOff = 1
700 types.Func.SetLinkage(linkageGlobal)
701
702 btf := marshalBTF(&types, strings, internal.NativeEndian)
703
704 fd, err := bpfLoadBTF(&bpfLoadBTFAttr{
705 btf: internal.NewSlicePointer(btf),
706 btfSize: uint32(len(btf)),
707 })
708 if err == nil {
709 fd.Close()
710 }
711
712 // Check for EINVAL specifically, rather than err != nil since we
713 // otherwise misdetect due to insufficient permissions.
714 return !errors.Is(err, unix.EINVAL), nil
715 })
33 "bytes"
44 "compress/gzip"
55 "encoding/binary"
6 "errors"
67 "fmt"
78 "io/ioutil"
89 "os"
910 "testing"
1011
12 "github.com/cilium/ebpf/internal"
1113 "github.com/cilium/ebpf/internal/testutils"
1214 )
1315
2830 t.Fatal(err)
2931 }
3032
31 _, _, err = parseBTF(bytes.NewReader(buf), binary.LittleEndian)
33 _, err = loadNakedSpec(bytes.NewReader(buf), binary.LittleEndian, nil, nil)
3234 if err != nil {
3335 t.Fatal("Can't load BTF:", err)
3436 }
3537 }
3638
3739 func TestParseCurrentKernelBTF(t *testing.T) {
38 if _, err := os.Stat("/sys/kernel/btf/vmlinux"); os.IsNotExist(err) {
39 t.Skip("/sys/kernel/btf/vmlinux is not available")
40 spec, err := loadKernelSpec()
41 if errors.Is(err, ErrNotFound) {
42 t.Skip("BTF is not available:", err)
43 }
44 if err != nil {
45 t.Fatal("Can't load BTF:", err)
4046 }
4147
42 fh, err := os.Open("/sys/kernel/btf/vmlinux")
43 if err != nil {
44 t.Fatal(err)
45 }
46 defer fh.Close()
47
48 _, _, err = parseBTF(fh, binary.LittleEndian)
49 if err != nil {
50 t.Fatal("Can't load BTF:", err)
48 if len(spec.types) == 0 {
49 t.Fatal("Empty kernel BTF")
5150 }
5251 }
5352
5453 func TestLoadSpecFromElf(t *testing.T) {
55 fh, err := os.Open("../../testdata/loader-clang-9.elf")
56 if err != nil {
57 t.Fatal(err)
58 }
59 defer fh.Close()
54 testutils.TestFiles(t, "../../testdata/loader-clang-9-*.elf", func(t *testing.T, file string) {
55 fh, err := os.Open(file)
56 if err != nil {
57 t.Fatal(err)
58 }
59 defer fh.Close()
6060
61 spec, err := LoadSpecFromReader(fh)
62 if err != nil {
63 t.Fatal("Can't load BTF:", err)
64 }
65
66 if spec == nil {
67 t.Error("No BTF found in ELF")
68 }
69
70 if sec, err := spec.Program("xdp", 1); err != nil {
71 t.Error("Can't get BTF for the xdp section:", err)
72 } else if sec == nil {
73 t.Error("Missing BTF for the xdp section")
74 }
75
76 if sec, err := spec.Program("socket", 1); err != nil {
77 t.Error("Can't get BTF for the socket section:", err)
78 } else if sec == nil {
79 t.Error("Missing BTF for the socket section")
80 }
81
82 var bpfMapDef Struct
83 if err := spec.FindType("bpf_map_def", &bpfMapDef); err != nil {
84 t.Fatal("Can't find bpf_map_def:", err)
85 }
86
87 if name := bpfMapDef.Name; name != "bpf_map_def" {
88 t.Error("struct bpf_map_def has incorrect name:", name)
89 }
90
91 t.Run("Handle", func(t *testing.T) {
92 btf, err := NewHandle(spec)
93 testutils.SkipIfNotSupported(t, err)
61 spec, err := LoadSpecFromReader(fh)
9462 if err != nil {
9563 t.Fatal("Can't load BTF:", err)
9664 }
97 defer btf.Close()
65
66 if spec == nil {
67 t.Error("No BTF found in ELF")
68 }
69
70 if sec, err := spec.Program("xdp", 1); err != nil {
71 t.Error("Can't get BTF for the xdp section:", err)
72 } else if sec == nil {
73 t.Error("Missing BTF for the xdp section")
74 }
75
76 if sec, err := spec.Program("socket", 1); err != nil {
77 t.Error("Can't get BTF for the socket section:", err)
78 } else if sec == nil {
79 t.Error("Missing BTF for the socket section")
80 }
81
82 var bpfMapDef Struct
83 if err := spec.FindType("bpf_map_def", &bpfMapDef); err != nil {
84 t.Error("Can't find bpf_map_def:", err)
85 }
86
87 var tmp Void
88 if err := spec.FindType("totally_bogus_type", &tmp); !errors.Is(err, ErrNotFound) {
89 t.Error("FindType doesn't return ErrNotFound:", err)
90 }
91
92 if spec.byteOrder != internal.NativeEndian {
93 return
94 }
95
96 t.Run("Handle", func(t *testing.T) {
97 btf, err := NewHandle(spec)
98 testutils.SkipIfNotSupported(t, err)
99 if err != nil {
100 t.Fatal("Can't load BTF:", err)
101 }
102 defer btf.Close()
103 })
98104 })
99105 }
100106
101107 func TestHaveBTF(t *testing.T) {
102108 testutils.CheckFeatureTest(t, haveBTF)
109 }
110
111 func TestHaveFuncLinkage(t *testing.T) {
112 testutils.CheckFeatureTest(t, haveFuncLinkage)
103113 }
104114
105115 func ExampleSpec_FindType() {
33 "encoding/binary"
44 "fmt"
55 "io"
6
7 "golang.org/x/xerrors"
86 )
97
108 // btfKind describes a Type.
3230 kindDatasec
3331 )
3432
33 type btfFuncLinkage uint8
34
35 const (
36 linkageStatic btfFuncLinkage = iota
37 linkageGlobal
38 linkageExtern
39 )
40
3541 const (
3642 btfTypeKindShift = 24
3743 btfTypeKindLen = 4
4349 type btfType struct {
4450 NameOff uint32
4551 /* "info" bits arrangement
46 * bits 0-15: vlen (e.g. # of struct's members)
52 * bits 0-15: vlen (e.g. # of struct's members), linkage
4753 * bits 16-23: unused
4854 * bits 24-27: kind (e.g. int, ptr, array...etc)
4955 * bits 28-30: unused
129135 bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift)
130136 }
131137
138 func (bt *btfType) Linkage() btfFuncLinkage {
139 return btfFuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift))
140 }
141
142 func (bt *btfType) SetLinkage(linkage btfFuncLinkage) {
143 bt.setInfo(uint32(linkage), btfTypeVlenMask, btfTypeVlenShift)
144 }
145
132146 func (bt *btfType) Type() TypeID {
133147 // TODO: Panic here if wrong kind?
134148 return TypeID(bt.SizeType)
176190
177191 type btfVariable struct {
178192 Linkage uint32
193 }
194
195 type btfEnum struct {
196 NameOff uint32
197 Val int32
198 }
199
200 type btfParam struct {
201 NameOff uint32
202 Type TypeID
179203 }
180204
181205 func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
188212 if err := binary.Read(r, bo, &header); err == io.EOF {
189213 return types, nil
190214 } else if err != nil {
191 return nil, xerrors.Errorf("can't read type info for id %v: %v", id, err)
215 return nil, fmt.Errorf("can't read type info for id %v: %v", id, err)
192216 }
193217
194218 var data interface{}
195219 switch header.Kind() {
196220 case kindInt:
197 // sizeof(uint32)
198 data = make([]byte, 4)
221 data = new(uint32)
199222 case kindPointer:
200223 case kindArray:
201224 data = new(btfArray)
204227 case kindUnion:
205228 data = make([]btfMember, header.Vlen())
206229 case kindEnum:
207 // sizeof(struct btf_enum)
208 data = make([]byte, header.Vlen()*4*2)
230 data = make([]btfEnum, header.Vlen())
209231 case kindForward:
210232 case kindTypedef:
211233 case kindVolatile:
213235 case kindRestrict:
214236 case kindFunc:
215237 case kindFuncProto:
216 // sizeof(struct btf_param)
217 data = make([]byte, header.Vlen()*4*2)
238 data = make([]btfParam, header.Vlen())
218239 case kindVar:
219240 data = new(btfVariable)
220241 case kindDatasec:
221242 data = make([]btfVarSecinfo, header.Vlen())
222243 default:
223 return nil, xerrors.Errorf("type id %v: unknown kind: %v", id, header.Kind())
244 return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind())
224245 }
225246
226247 if data == nil {
229250 }
230251
231252 if err := binary.Read(r, bo, data); err != nil {
232 return nil, xerrors.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err)
253 return nil, fmt.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err)
233254 }
234255
235256 types = append(types, rawType{header, data})
22 import (
33 "bytes"
44 "encoding/binary"
5 "errors"
6 "fmt"
57 "io"
68 "io/ioutil"
79
810 "github.com/cilium/ebpf/asm"
911 "github.com/cilium/ebpf/internal"
10
11 "golang.org/x/xerrors"
1212 )
1313
1414 type btfExtHeader struct {
2626 func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, err error) {
2727 var header btfExtHeader
2828 if err := binary.Read(r, bo, &header); err != nil {
29 return nil, nil, xerrors.Errorf("can't read header: %v", err)
29 return nil, nil, fmt.Errorf("can't read header: %v", err)
3030 }
3131
3232 if header.Magic != btfMagic {
33 return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic)
33 return nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic)
3434 }
3535
3636 if header.Version != 1 {
37 return nil, nil, xerrors.Errorf("unexpected version %v", header.Version)
37 return nil, nil, fmt.Errorf("unexpected version %v", header.Version)
3838 }
3939
4040 if header.Flags != 0 {
41 return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags)
41 return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags)
4242 }
4343
4444 remainder := int64(header.HdrLen) - int64(binary.Size(&header))
4545 if remainder < 0 {
46 return nil, nil, xerrors.New("header is too short")
46 return nil, nil, errors.New("header is too short")
4747 }
4848
4949 // Of course, the .BTF.ext header has different semantics than the
5050 // .BTF ext header. We need to ignore non-null values.
5151 _, err = io.CopyN(ioutil.Discard, r, remainder)
5252 if err != nil {
53 return nil, nil, xerrors.Errorf("header padding: %v", err)
53 return nil, nil, fmt.Errorf("header padding: %v", err)
5454 }
5555
5656 if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil {
57 return nil, nil, xerrors.Errorf("can't seek to function info section: %v", err)
57 return nil, nil, fmt.Errorf("can't seek to function info section: %v", err)
5858 }
5959
6060 funcInfo, err = parseExtInfo(io.LimitReader(r, int64(header.FuncInfoLen)), bo, strings)
6161 if err != nil {
62 return nil, nil, xerrors.Errorf("function info: %w", err)
62 return nil, nil, fmt.Errorf("function info: %w", err)
6363 }
6464
6565 if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil {
66 return nil, nil, xerrors.Errorf("can't seek to line info section: %v", err)
66 return nil, nil, fmt.Errorf("can't seek to line info section: %v", err)
6767 }
6868
6969 lineInfo, err = parseExtInfo(io.LimitReader(r, int64(header.LineInfoLen)), bo, strings)
7070 if err != nil {
71 return nil, nil, xerrors.Errorf("line info: %w", err)
71 return nil, nil, fmt.Errorf("line info: %w", err)
7272 }
7373
7474 return funcInfo, lineInfo, nil
9191
9292 func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) {
9393 if other.recordSize != ei.recordSize {
94 return extInfo{}, xerrors.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize)
94 return extInfo{}, fmt.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize)
9595 }
9696
9797 records := make([]extInfoRecord, 0, len(ei.records)+len(other.records))
116116 // while the ELF tracks it in bytes.
117117 insnOff := uint32(info.InsnOff / asm.InstructionSize)
118118 if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil {
119 return nil, xerrors.Errorf("can't write instruction offset: %v", err)
119 return nil, fmt.Errorf("can't write instruction offset: %v", err)
120120 }
121121
122122 buf.Write(info.Opaque)
128128 func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) {
129129 var recordSize uint32
130130 if err := binary.Read(r, bo, &recordSize); err != nil {
131 return nil, xerrors.Errorf("can't read record size: %v", err)
131 return nil, fmt.Errorf("can't read record size: %v", err)
132132 }
133133
134134 if recordSize < 4 {
135135 // Need at least insnOff
136 return nil, xerrors.New("record size too short")
136 return nil, errors.New("record size too short")
137137 }
138138
139139 result := make(map[string]extInfo)
142142 if err := binary.Read(r, bo, &infoHeader); err == io.EOF {
143143 return result, nil
144144 } else if err != nil {
145 return nil, xerrors.Errorf("can't read ext info header: %v", err)
145 return nil, fmt.Errorf("can't read ext info header: %v", err)
146146 }
147147
148148 secName, err := strings.Lookup(infoHeader.SecNameOff)
149149 if err != nil {
150 return nil, xerrors.Errorf("can't get section name: %w", err)
150 return nil, fmt.Errorf("can't get section name: %w", err)
151151 }
152152
153153 if infoHeader.NumInfo == 0 {
154 return nil, xerrors.Errorf("section %s has invalid number of records", secName)
154 return nil, fmt.Errorf("section %s has invalid number of records", secName)
155155 }
156156
157157 var records []extInfoRecord
158158 for i := uint32(0); i < infoHeader.NumInfo; i++ {
159159 var byteOff uint32
160160 if err := binary.Read(r, bo, &byteOff); err != nil {
161 return nil, xerrors.Errorf("section %v: can't read extended info offset: %v", secName, err)
161 return nil, fmt.Errorf("section %v: can't read extended info offset: %v", secName, err)
162162 }
163163
164164 buf := make([]byte, int(recordSize-4))
165165 if _, err := io.ReadFull(r, buf); err != nil {
166 return nil, xerrors.Errorf("section %v: can't read record: %v", secName, err)
166 return nil, fmt.Errorf("section %v: can't read record: %v", secName, err)
167167 }
168168
169169 if byteOff%asm.InstructionSize != 0 {
170 return nil, xerrors.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff)
170 return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff)
171171 }
172172
173173 records = append(records, extInfoRecord{uint64(byteOff), buf})
11
22 import (
33 "bytes"
4 "errors"
5 "fmt"
46 "io"
57 "io/ioutil"
6
7 "golang.org/x/xerrors"
88 )
99
1010 type stringTable []byte
1212 func readStringTable(r io.Reader) (stringTable, error) {
1313 contents, err := ioutil.ReadAll(r)
1414 if err != nil {
15 return nil, xerrors.Errorf("can't read string table: %v", err)
15 return nil, fmt.Errorf("can't read string table: %v", err)
1616 }
1717
1818 if len(contents) < 1 {
19 return nil, xerrors.New("string table is empty")
19 return nil, errors.New("string table is empty")
2020 }
2121
2222 if contents[0] != '\x00' {
23 return nil, xerrors.New("first item in string table is non-empty")
23 return nil, errors.New("first item in string table is non-empty")
2424 }
2525
2626 if contents[len(contents)-1] != '\x00' {
27 return nil, xerrors.New("string table isn't null terminated")
27 return nil, errors.New("string table isn't null terminated")
2828 }
2929
3030 return stringTable(contents), nil
3232
3333 func (st stringTable) Lookup(offset uint32) (string, error) {
3434 if int64(offset) > int64(^uint(0)>>1) {
35 return "", xerrors.Errorf("offset %d overflows int", offset)
35 return "", fmt.Errorf("offset %d overflows int", offset)
3636 }
3737
3838 pos := int(offset)
3939 if pos >= len(st) {
40 return "", xerrors.Errorf("offset %d is out of bounds", offset)
40 return "", fmt.Errorf("offset %d is out of bounds", offset)
4141 }
4242
4343 if pos > 0 && st[pos-1] != '\x00' {
44 return "", xerrors.Errorf("offset %d isn't start of a string", offset)
44 return "", fmt.Errorf("offset %d isn't start of a string", offset)
4545 }
4646
4747 str := st[pos:]
4848 end := bytes.IndexByte(str, '\x00')
4949 if end == -1 {
50 return "", xerrors.Errorf("offset %d isn't null terminated", offset)
50 return "", fmt.Errorf("offset %d isn't null terminated", offset)
5151 }
5252
5353 return string(str[:end]), nil
00 package btf
11
22 import (
3 "errors"
4 "fmt"
35 "math"
4
5 "golang.org/x/xerrors"
66 )
77
88 const maxTypeDepth = 32
3737 // Void is the unit type of BTF.
3838 type Void struct{}
3939
40 func (v Void) ID() TypeID { return 0 }
41 func (v Void) copy() Type { return Void{} }
42 func (v Void) walk(*copyStack) {}
40 func (v *Void) ID() TypeID { return 0 }
41 func (v *Void) size() uint32 { return 0 }
42 func (v *Void) copy() Type { return (*Void)(nil) }
43 func (v *Void) walk(*copyStack) {}
4344
4445 // Int is an integer of a given length.
4546 type Int struct {
309310 switch v := typ.(type) {
310311 case *Array:
311312 if n > 0 && int64(v.Nelems) > math.MaxInt64/n {
312 return 0, xerrors.New("overflow")
313 return 0, errors.New("overflow")
313314 }
314315
315316 // Arrays may be of zero length, which allows
335336 continue
336337
337338 default:
338 return 0, xerrors.Errorf("unrecognized type %T", typ)
339 return 0, fmt.Errorf("unrecognized type %T", typ)
339340 }
340341
341342 if n > 0 && elem > math.MaxInt64/n {
342 return 0, xerrors.New("overflow")
343 return 0, errors.New("overflow")
343344 }
344345
345346 size := n * elem
346347 if int64(int(size)) != size {
347 return 0, xerrors.New("overflow")
348 return 0, errors.New("overflow")
348349 }
349350
350351 return int(size), nil
351352 }
352353
353 return 0, xerrors.New("exceeded type depth")
354 return 0, errors.New("exceeded type depth")
354355 }
355356
356357 // copy a Type recursively.
432433 for i, btfMember := range raw {
433434 name, err := rawStrings.LookupName(btfMember.NameOff)
434435 if err != nil {
435 return nil, xerrors.Errorf("can't get name for member %d: %w", i, err)
436 return nil, fmt.Errorf("can't get name for member %d: %w", i, err)
436437 }
437438 members = append(members, Member{
438439 Name: name,
446447 }
447448
448449 types := make([]Type, 0, len(rawTypes))
449 types = append(types, Void{})
450 types = append(types, (*Void)(nil))
450451 namedTypes = make(map[string][]Type)
451452
452453 for i, raw := range rawTypes {
459460
460461 name, err := rawStrings.LookupName(raw.NameOff)
461462 if err != nil {
462 return nil, xerrors.Errorf("can't get name for type id %d: %w", id, err)
463 return nil, fmt.Errorf("can't get name for type id %d: %w", id, err)
463464 }
464465
465466 switch raw.Kind() {
483484 case kindStruct:
484485 members, err := convertMembers(raw.data.([]btfMember))
485486 if err != nil {
486 return nil, xerrors.Errorf("struct %s (id %d): %w", name, id, err)
487 return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err)
487488 }
488489 typ = &Struct{id, name, raw.Size(), members}
489490
490491 case kindUnion:
491492 members, err := convertMembers(raw.data.([]btfMember))
492493 if err != nil {
493 return nil, xerrors.Errorf("union %s (id %d): %w", name, id, err)
494 return nil, fmt.Errorf("union %s (id %d): %w", name, id, err)
494495 }
495496 typ = &Union{id, name, raw.Size(), members}
496497
550551 typ = &Datasec{id, name, raw.SizeType, vars}
551552
552553 default:
553 return nil, xerrors.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
554 return nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
554555 }
555556
556557 types = append(types, typ)
565566 for _, fixup := range fixups {
566567 i := int(fixup.id)
567568 if i >= len(types) {
568 return nil, xerrors.Errorf("reference to invalid type id: %d", fixup.id)
569 return nil, fmt.Errorf("reference to invalid type id: %d", fixup.id)
569570 }
570571
571572 // Default void (id 0) to unknown
575576 }
576577
577578 if expected := fixup.expectedKind; expected != kindUnknown && rawKind != expected {
578 return nil, xerrors.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind)
579 return nil, fmt.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind)
579580 }
580581
581582 *fixup.typ = types[i]
00 package btf
11
2 import "testing"
3
4 import "fmt"
2 import (
3 "fmt"
4 "testing"
5 )
56
67 func TestSizeof(t *testing.T) {
78 testcases := []struct {
89 size int
910 typ Type
1011 }{
12 {0, (*Void)(nil)},
1113 {1, &Int{Size: 1}},
1214 {4, &Enum{}},
13 {0, &Array{Type: &Pointer{Target: Void{}}, Nelems: 0}},
15 {0, &Array{Type: &Pointer{Target: (*Void)(nil)}, Nelems: 0}},
1416 {12, &Array{Type: &Enum{}, Nelems: 3}},
1517 }
1618
2931 }
3032
3133 func TestCopyType(t *testing.T) {
32 _ = copyType(Void{})
34 _ = copyType((*Void)(nil))
3335
3436 in := &Int{Size: 4}
3537 out := copyType(in)
11
22 import (
33 "bytes"
4 "errors"
45 "fmt"
56 "strings"
67
78 "github.com/cilium/ebpf/internal/unix"
8 "golang.org/x/xerrors"
99 )
1010
1111 // ErrorWithLog returns an error that includes logs from the
1515 // the log. It is used to check for truncation of the output.
1616 func ErrorWithLog(err error, log []byte, logErr error) error {
1717 logStr := strings.Trim(CString(log), "\t\r\n ")
18 if xerrors.Is(logErr, unix.ENOSPC) {
18 if errors.Is(logErr, unix.ENOSPC) {
1919 logStr += " (truncated...)"
2020 }
2121
00 package internal
11
22 import (
3 "errors"
4 "fmt"
5 "os"
36 "runtime"
47 "strconv"
58
69 "github.com/cilium/ebpf/internal/unix"
7
8 "golang.org/x/xerrors"
910 )
1011
11 var ErrClosedFd = xerrors.New("use of closed file descriptor")
12 var ErrClosedFd = errors.New("use of closed file descriptor")
1213
1314 type FD struct {
1415 raw int64
5556
5657 dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0)
5758 if err != nil {
58 return nil, xerrors.Errorf("can't dup fd: %v", err)
59 return nil, fmt.Errorf("can't dup fd: %v", err)
5960 }
6061
6162 return NewFD(uint32(dup)), nil
6263 }
64
65 func (fd *FD) File(name string) *os.File {
66 fd.Forget()
67 return os.NewFile(uintptr(fd.raw), name)
68 }
00 package internal
11
22 import (
3 "errors"
34 "fmt"
45 "sync"
5
6 "golang.org/x/xerrors"
76 )
87
98 // ErrNotSupported indicates that a feature is not supported by the current kernel.
10 var ErrNotSupported = xerrors.New("not supported")
9 var ErrNotSupported = errors.New("not supported")
1110
1211 // UnsupportedFeatureError is returned by FeatureTest() functions.
1312 type UnsupportedFeatureError struct {
2827 return target == ErrNotSupported
2928 }
3029
30 type featureTest struct {
31 sync.Mutex
32 successful bool
33 result error
34 }
35
36 // FeatureTestFn is used to determine whether the kernel supports
37 // a certain feature.
38 //
39 // The return values have the following semantics:
40 //
41 // err != nil: the test couldn't be executed
42 // err == nil && available: the feature is available
43 // err == nil && !available: the feature isn't available
44 type FeatureTestFn func() (available bool, err error)
45
3146 // FeatureTest wraps a function so that it is run at most once.
3247 //
3348 // name should identify the tested feature, while version must be in the
3449 // form Major.Minor[.Patch].
3550 //
36 // Returns a descriptive UnsupportedFeatureError if the feature is not available.
37 func FeatureTest(name, version string, fn func() bool) func() error {
51 // Returns an error wrapping ErrNotSupported if the feature is not supported.
52 func FeatureTest(name, version string, fn FeatureTestFn) func() error {
3853 v, err := NewVersion(version)
3954 if err != nil {
4055 return func() error { return err }
4156 }
4257
43 var (
44 once sync.Once
45 result error
46 )
58 ft := new(featureTest)
59 return func() error {
60 ft.Lock()
61 defer ft.Unlock()
4762
48 return func() error {
49 once.Do(func() {
50 if !fn() {
51 result = &UnsupportedFeatureError{
52 MinimumVersion: v,
53 Name: name,
54 }
63 if ft.successful {
64 return ft.result
65 }
66
67 available, err := fn()
68 if errors.Is(err, ErrNotSupported) {
69 // The feature test aborted because a dependent feature
70 // is missing, which we should cache.
71 available = false
72 } else if err != nil {
73 // We couldn't execute the feature test to a point
74 // where it could make a determination.
75 // Don't cache the result, just return it.
76 return fmt.Errorf("can't detect support for %s: %w", name, err)
77 }
78
79 ft.successful = true
80 if !available {
81 ft.result = &UnsupportedFeatureError{
82 MinimumVersion: v,
83 Name: name,
5584 }
56 })
57 return result
85 }
86 return ft.result
5887 }
5988 }
6089
6897 var major, minor, patch uint16
6998 n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch)
7099 if n < 2 {
71 return Version{}, xerrors.Errorf("invalid version: %s", ver)
100 return Version{}, fmt.Errorf("invalid version: %s", ver)
72101 }
73102 return Version{major, minor, patch}, nil
74103 }
00 package internal
11
22 import (
3 "errors"
4 "fmt"
35 "strings"
46 "testing"
5
6 "golang.org/x/xerrors"
77 )
88
99 func TestFeatureTest(t *testing.T) {
1010 var called bool
1111
12 fn := FeatureTest("foo", "1.0", func() bool {
12 fn := FeatureTest("foo", "1.0", func() (bool, error) {
1313 called = true
14 return true
14 return true, nil
1515 })
1616
1717 if called {
2727 t.Error("Unexpected negative result:", err)
2828 }
2929
30 fn = FeatureTest("bar", "2.1.1", func() bool {
31 return false
30 fn = FeatureTest("bar", "2.1.1", func() (bool, error) {
31 return false, nil
3232 })
3333
3434 err = fn()
4545 t.Error("UnsupportedFeatureError.Error doesn't contain version")
4646 }
4747
48 if !xerrors.Is(err, ErrNotSupported) {
48 if !errors.Is(err, ErrNotSupported) {
4949 t.Error("UnsupportedFeatureError is not ErrNotSupported")
50 }
51
52 fn = FeatureTest("bar", "2.1.1", func() (bool, error) {
53 return false, errors.New("foo")
54 })
55
56 err1, err2 := fn(), fn()
57 if err1 == err2 {
58 t.Error("Cached result of unsuccessful execution")
59 }
60
61 fn = FeatureTest("bar", "2.1.1", func() (bool, error) {
62 return false, fmt.Errorf("bar: %w", ErrNotSupported)
63 })
64
65 err1, err2 = fn(), fn()
66 if err1 != err2 {
67 t.Error("Didn't cache an error wrapping ErrNotSupported")
5068 }
5169 }
5270
00 package internal
11
2 import "golang.org/x/xerrors"
2 import "errors"
33
44 // DiscardZeroes makes sure that all written bytes are zero
55 // before discarding them.
88 func (DiscardZeroes) Write(p []byte) (int, error) {
99 for _, b := range p {
1010 if b != 0 {
11 return 0, xerrors.New("encountered non-zero byte")
11 return 0, errors.New("encountered non-zero byte")
1212 }
1313 }
1414 return len(p), nil
00 package internal
11
22 import (
3 "fmt"
4 "path/filepath"
35 "runtime"
46 "unsafe"
57
68 "github.com/cilium/ebpf/internal/unix"
79 )
810
11 //go:generate stringer -output syscall_string.go -type=BPFCmd
12
13 // BPFCmd identifies a subcommand of the bpf syscall.
14 type BPFCmd int
15
16 // Well known BPF commands.
17 const (
18 BPF_MAP_CREATE BPFCmd = iota
19 BPF_MAP_LOOKUP_ELEM
20 BPF_MAP_UPDATE_ELEM
21 BPF_MAP_DELETE_ELEM
22 BPF_MAP_GET_NEXT_KEY
23 BPF_PROG_LOAD
24 BPF_OBJ_PIN
25 BPF_OBJ_GET
26 BPF_PROG_ATTACH
27 BPF_PROG_DETACH
28 BPF_PROG_TEST_RUN
29 BPF_PROG_GET_NEXT_ID
30 BPF_MAP_GET_NEXT_ID
31 BPF_PROG_GET_FD_BY_ID
32 BPF_MAP_GET_FD_BY_ID
33 BPF_OBJ_GET_INFO_BY_FD
34 BPF_PROG_QUERY
35 BPF_RAW_TRACEPOINT_OPEN
36 BPF_BTF_LOAD
37 BPF_BTF_GET_FD_BY_ID
38 BPF_TASK_FD_QUERY
39 BPF_MAP_LOOKUP_AND_DELETE_ELEM
40 BPF_MAP_FREEZE
41 BPF_BTF_GET_NEXT_ID
42 BPF_MAP_LOOKUP_BATCH
43 BPF_MAP_LOOKUP_AND_DELETE_BATCH
44 BPF_MAP_UPDATE_BATCH
45 BPF_MAP_DELETE_BATCH
46 BPF_LINK_CREATE
47 BPF_LINK_UPDATE
48 BPF_LINK_GET_FD_BY_ID
49 BPF_LINK_GET_NEXT_ID
50 BPF_ENABLE_STATS
51 BPF_ITER_CREATE
52 )
53
954 // BPF wraps SYS_BPF.
1055 //
1156 // Any pointers contained in attr must use the Pointer type from this package.
12 func BPF(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) {
57 func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
1358 r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
1459 runtime.KeepAlive(attr)
1560
2065
2166 return r1, err
2267 }
68
69 type BPFProgAttachAttr struct {
70 TargetFd uint32
71 AttachBpfFd uint32
72 AttachType uint32
73 AttachFlags uint32
74 ReplaceBpfFd uint32
75 }
76
77 func BPFProgAttach(attr *BPFProgAttachAttr) error {
78 _, err := BPF(BPF_PROG_ATTACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
79 return err
80 }
81
82 type BPFProgDetachAttr struct {
83 TargetFd uint32
84 AttachBpfFd uint32
85 AttachType uint32
86 }
87
88 func BPFProgDetach(attr *BPFProgDetachAttr) error {
89 _, err := BPF(BPF_PROG_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
90 return err
91 }
92
93 type bpfObjAttr struct {
94 fileName Pointer
95 fd uint32
96 fileFlags uint32
97 }
98
99 const bpfFSType = 0xcafe4a11
100
101 // BPFObjPin wraps BPF_OBJ_PIN.
102 func BPFObjPin(fileName string, fd *FD) error {
103 dirName := filepath.Dir(fileName)
104 var statfs unix.Statfs_t
105 if err := unix.Statfs(dirName, &statfs); err != nil {
106 return err
107 }
108 if uint64(statfs.Type) != bpfFSType {
109 return fmt.Errorf("%s is not on a bpf filesystem", fileName)
110 }
111
112 value, err := fd.Value()
113 if err != nil {
114 return err
115 }
116
117 attr := bpfObjAttr{
118 fileName: NewStringPointer(fileName),
119 fd: value,
120 }
121 _, err = BPF(BPF_OBJ_PIN, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
122 if err != nil {
123 return fmt.Errorf("pin object %s: %w", fileName, err)
124 }
125 return nil
126 }
127
128 // BPFObjGet wraps BPF_OBJ_GET.
129 func BPFObjGet(fileName string) (*FD, error) {
130 attr := bpfObjAttr{
131 fileName: NewStringPointer(fileName),
132 }
133 ptr, err := BPF(BPF_OBJ_GET, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
134 if err != nil {
135 return nil, fmt.Errorf("get object %s: %w", fileName, err)
136 }
137 return NewFD(uint32(ptr)), nil
138 }
0 // Code generated by "stringer -output syscall_string.go -type=BPFCmd"; DO NOT EDIT.
1
2 package internal
3
4 import "strconv"
5
6 func _() {
7 // An "invalid array index" compiler error signifies that the constant values have changed.
8 // Re-run the stringer command to generate them again.
9 var x [1]struct{}
10 _ = x[BPF_MAP_CREATE-0]
11 _ = x[BPF_MAP_LOOKUP_ELEM-1]
12 _ = x[BPF_MAP_UPDATE_ELEM-2]
13 _ = x[BPF_MAP_DELETE_ELEM-3]
14 _ = x[BPF_MAP_GET_NEXT_KEY-4]
15 _ = x[BPF_PROG_LOAD-5]
16 _ = x[BPF_OBJ_PIN-6]
17 _ = x[BPF_OBJ_GET-7]
18 _ = x[BPF_PROG_ATTACH-8]
19 _ = x[BPF_PROG_DETACH-9]
20 _ = x[BPF_PROG_TEST_RUN-10]
21 _ = x[BPF_PROG_GET_NEXT_ID-11]
22 _ = x[BPF_MAP_GET_NEXT_ID-12]
23 _ = x[BPF_PROG_GET_FD_BY_ID-13]
24 _ = x[BPF_MAP_GET_FD_BY_ID-14]
25 _ = x[BPF_OBJ_GET_INFO_BY_FD-15]
26 _ = x[BPF_PROG_QUERY-16]
27 _ = x[BPF_RAW_TRACEPOINT_OPEN-17]
28 _ = x[BPF_BTF_LOAD-18]
29 _ = x[BPF_BTF_GET_FD_BY_ID-19]
30 _ = x[BPF_TASK_FD_QUERY-20]
31 _ = x[BPF_MAP_LOOKUP_AND_DELETE_ELEM-21]
32 _ = x[BPF_MAP_FREEZE-22]
33 _ = x[BPF_BTF_GET_NEXT_ID-23]
34 _ = x[BPF_MAP_LOOKUP_BATCH-24]
35 _ = x[BPF_MAP_LOOKUP_AND_DELETE_BATCH-25]
36 _ = x[BPF_MAP_UPDATE_BATCH-26]
37 _ = x[BPF_MAP_DELETE_BATCH-27]
38 _ = x[BPF_LINK_CREATE-28]
39 _ = x[BPF_LINK_UPDATE-29]
40 _ = x[BPF_LINK_GET_FD_BY_ID-30]
41 _ = x[BPF_LINK_GET_NEXT_ID-31]
42 _ = x[BPF_ENABLE_STATS-32]
43 _ = x[BPF_ITER_CREATE-33]
44 }
45
46 const _BPFCmd_name = "BPF_MAP_CREATEBPF_MAP_LOOKUP_ELEMBPF_MAP_UPDATE_ELEMBPF_MAP_DELETE_ELEMBPF_MAP_GET_NEXT_KEYBPF_PROG_LOADBPF_OBJ_PINBPF_OBJ_GETBPF_PROG_ATTACHBPF_PROG_DETACHBPF_PROG_TEST_RUNBPF_PROG_GET_NEXT_IDBPF_MAP_GET_NEXT_IDBPF_PROG_GET_FD_BY_IDBPF_MAP_GET_FD_BY_IDBPF_OBJ_GET_INFO_BY_FDBPF_PROG_QUERYBPF_RAW_TRACEPOINT_OPENBPF_BTF_LOADBPF_BTF_GET_FD_BY_IDBPF_TASK_FD_QUERYBPF_MAP_LOOKUP_AND_DELETE_ELEMBPF_MAP_FREEZEBPF_BTF_GET_NEXT_IDBPF_MAP_LOOKUP_BATCHBPF_MAP_LOOKUP_AND_DELETE_BATCHBPF_MAP_UPDATE_BATCHBPF_MAP_DELETE_BATCHBPF_LINK_CREATEBPF_LINK_UPDATEBPF_LINK_GET_FD_BY_IDBPF_LINK_GET_NEXT_IDBPF_ENABLE_STATSBPF_ITER_CREATE"
47
48 var _BPFCmd_index = [...]uint16{0, 14, 33, 52, 71, 91, 104, 115, 126, 141, 156, 173, 193, 212, 233, 253, 275, 289, 312, 324, 344, 361, 391, 405, 424, 444, 475, 495, 515, 530, 545, 566, 586, 602, 617}
49
50 func (i BPFCmd) String() string {
51 if i < 0 || i >= BPFCmd(len(_BPFCmd_index)-1) {
52 return "BPFCmd(" + strconv.FormatInt(int64(i), 10) + ")"
53 }
54 return _BPFCmd_name[_BPFCmd_index[i]:_BPFCmd_index[i+1]]
55 }
11
22 import (
33 "bytes"
4 "errors"
45 "sync"
56 "testing"
67
78 "github.com/cilium/ebpf/internal"
89 "github.com/cilium/ebpf/internal/unix"
9 "golang.org/x/xerrors"
1010 )
1111
1212 var (
4242 return
4343 }
4444
45 ufe := err.(*internal.UnsupportedFeatureError)
46 checkKernelVersion(t, ufe)
45 var ufe *internal.UnsupportedFeatureError
46 if errors.As(err, &ufe) {
47 checkKernelVersion(t, ufe)
48 } else {
49 t.Error("Feature test failed:", err)
50 }
4751 }
4852
4953 func SkipIfNotSupported(tb testing.TB, err error) {
5054 var ufe *internal.UnsupportedFeatureError
51 if xerrors.As(err, &ufe) {
55 if errors.As(err, &ufe) {
5256 checkKernelVersion(tb, ufe)
5357 tb.Skip(ufe.Error())
58 }
59 if errors.Is(err, internal.ErrNotSupported) {
60 tb.Skip(err.Error())
5461 }
5562 }
5663
0 package testutils
1
2 import (
3 "path/filepath"
4 "testing"
5 )
6
7 // TestFiles calls fn for each file matching pattern.
8 //
9 // The function errors out if the pattern matches no files.
10 func TestFiles(t *testing.T, pattern string, fn func(*testing.T, string)) {
11 t.Helper()
12
13 files, err := filepath.Glob(pattern)
14 if err != nil {
15 t.Fatal("Can't glob files:", err)
16 }
17
18 if len(files) == 0 {
19 t.Fatalf("Pattern %s matched no files", pattern)
20 }
21
22 for _, f := range files {
23 file := f // force copy
24 name := filepath.Base(file)
25 t.Run(name, func(t *testing.T) {
26 fn(t, file)
27 })
28 }
29 }
99
1010 const (
1111 ENOENT = linux.ENOENT
12 EEXIST = linux.EEXIST
1213 EAGAIN = linux.EAGAIN
1314 ENOSPC = linux.ENOSPC
1415 EINVAL = linux.EINVAL
1516 EPOLLIN = linux.EPOLLIN
1617 EINTR = linux.EINTR
18 EPERM = linux.EPERM
1719 ESRCH = linux.ESRCH
1820 ENODEV = linux.ENODEV
1921 BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG
3537 PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW
3638 PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC
3739 RLIM_INFINITY = linux.RLIM_INFINITY
40 RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK
3841 )
3942
4043 // Statfs_t is a wrapper
1111
1212 const (
1313 ENOENT = syscall.ENOENT
14 EEXIST = syscall.EEXIST
1415 EAGAIN = syscall.EAGAIN
1516 ENOSPC = syscall.ENOSPC
1617 EINVAL = syscall.EINVAL
1718 EINTR = syscall.EINTR
19 EPERM = syscall.EPERM
1820 ESRCH = syscall.ESRCH
1921 ENODEV = syscall.ENODEV
2022 BPF_F_RDONLY_PROG = 0
3739 PERF_SAMPLE_RAW = 0x400
3840 PERF_FLAG_FD_CLOEXEC = 0x8
3941 RLIM_INFINITY = 0x7fffffffffffffff
42 RLIMIT_MEMLOCK = 8
4043 )
4144
4245 // Statfs_t is a wrapper
0 package link
1
2 import (
3 "errors"
4 "fmt"
5 "os"
6
7 "github.com/cilium/ebpf"
8 )
9
10 type cgroupAttachFlags uint32
11
12 // cgroup attach flags
13 const (
14 flagAllowOverride cgroupAttachFlags = 1 << iota
15 flagAllowMulti
16 flagReplace
17 )
18
19 type CgroupOptions struct {
20 // Path to a cgroupv2 folder.
21 Path string
22 // One of the AttachCgroup* constants
23 Attach ebpf.AttachType
24 // Program must be of type CGroup*, and the attach type must match Attach.
25 Program *ebpf.Program
26 }
27
28 // AttachCgroup links a BPF program to a cgroup.
29 func AttachCgroup(opts CgroupOptions) (Link, error) {
30 cgroup, err := os.Open(opts.Path)
31 if err != nil {
32 return nil, fmt.Errorf("can't open cgroup: %s", err)
33 }
34
35 clone, err := opts.Program.Clone()
36 if err != nil {
37 cgroup.Close()
38 return nil, err
39 }
40
41 var cg Link
42 cg, err = newLinkCgroup(cgroup, opts.Attach, clone)
43 if errors.Is(err, ErrNotSupported) {
44 cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowMulti)
45 }
46 if errors.Is(err, ErrNotSupported) {
47 cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowOverride)
48 }
49 if err != nil {
50 cgroup.Close()
51 clone.Close()
52 return nil, err
53 }
54
55 return cg, nil
56 }
57
58 // LoadPinnedCgroup loads a pinned cgroup from a bpffs.
59 func LoadPinnedCgroup(fileName string) (Link, error) {
60 link, err := LoadPinnedRawLink(fileName)
61 if err != nil {
62 return nil, err
63 }
64
65 return &linkCgroup{link}, nil
66 }
67
68 type progAttachCgroup struct {
69 cgroup *os.File
70 current *ebpf.Program
71 attachType ebpf.AttachType
72 flags cgroupAttachFlags
73 }
74
75 var _ Link = (*progAttachCgroup)(nil)
76
77 func (cg *progAttachCgroup) isLink() {}
78
79 func newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) {
80 if flags&flagAllowMulti > 0 {
81 if err := haveProgAttachReplace(); err != nil {
82 return nil, fmt.Errorf("can't support multiple programs: %w", err)
83 }
84 }
85
86 err := RawAttachProgram(RawAttachProgramOptions{
87 Target: int(cgroup.Fd()),
88 Program: prog,
89 Flags: uint32(flags),
90 Attach: attach,
91 })
92 if err != nil {
93 return nil, fmt.Errorf("cgroup: %w", err)
94 }
95
96 return &progAttachCgroup{cgroup, prog, attach, flags}, nil
97 }
98
99 func (cg *progAttachCgroup) Close() error {
100 defer cg.cgroup.Close()
101 defer cg.current.Close()
102
103 err := RawDetachProgram(RawDetachProgramOptions{
104 Target: int(cg.cgroup.Fd()),
105 Program: cg.current,
106 Attach: cg.attachType,
107 })
108 if err != nil {
109 return fmt.Errorf("close cgroup: %s", err)
110 }
111 return nil
112 }
113
114 func (cg *progAttachCgroup) Update(prog *ebpf.Program) error {
115 new, err := prog.Clone()
116 if err != nil {
117 return err
118 }
119
120 args := RawAttachProgramOptions{
121 Target: int(cg.cgroup.Fd()),
122 Program: prog,
123 Attach: cg.attachType,
124 Flags: uint32(cg.flags),
125 }
126
127 if cg.flags&flagAllowMulti > 0 {
128 // Atomically replacing multiple programs requires at least
129 // 5.5 (commit 7dd68b3279f17921 "bpf: Support replacing cgroup-bpf
130 // program in MULTI mode")
131 args.Flags |= uint32(flagReplace)
132 args.Replace = cg.current
133 }
134
135 if err := RawAttachProgram(args); err != nil {
136 new.Close()
137 return fmt.Errorf("can't update cgroup: %s", err)
138 }
139
140 cg.current.Close()
141 cg.current = new
142 return nil
143 }
144
145 func (cg *progAttachCgroup) Pin(string) error {
146 return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
147 }
148
149 type linkCgroup struct {
150 *RawLink
151 }
152
153 var _ Link = (*linkCgroup)(nil)
154
155 func (cg *linkCgroup) isLink() {}
156
157 func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) {
158 link, err := AttachRawLink(RawLinkOptions{
159 Target: int(cgroup.Fd()),
160 Program: prog,
161 Attach: attach,
162 })
163 if err != nil {
164 return nil, err
165 }
166
167 return &linkCgroup{link}, err
168 }
0 package link
1
2 import (
3 "testing"
4
5 "github.com/cilium/ebpf"
6 "github.com/cilium/ebpf/internal/testutils"
7 )
8
9 func TestAttachCgroup(t *testing.T) {
10 cgroup, prog, cleanup := mustCgroupFixtures(t)
11 defer cleanup()
12
13 link, err := AttachCgroup(CgroupOptions{
14 Path: cgroup.Name(),
15 Attach: ebpf.AttachCGroupInetEgress,
16 Program: prog,
17 })
18 testutils.SkipIfNotSupported(t, err)
19 if err != nil {
20 t.Fatal(err)
21 }
22
23 if haveBPFLink() == nil {
24 if _, ok := link.(*linkCgroup); !ok {
25 t.Fatalf("Have support for bpf_link, but got %T instead of linkCgroup", link)
26 }
27 } else {
28 if _, ok := link.(*progAttachCgroup); !ok {
29 t.Fatalf("Expected progAttachCgroup, got %T instead", link)
30 }
31 }
32 }
33
34 func TestProgAttachCgroup(t *testing.T) {
35 cgroup, prog, cleanup := mustCgroupFixtures(t)
36 defer cleanup()
37
38 link, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, 0)
39 if err != nil {
40 t.Fatal("Can't create link:", err)
41 }
42
43 testLink(t, link, testLinkOptions{
44 prog: prog,
45 })
46 }
47
48 func TestProgAttachCgroupAllowMulti(t *testing.T) {
49 cgroup, prog, cleanup := mustCgroupFixtures(t)
50 defer cleanup()
51
52 link, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, flagAllowMulti)
53 testutils.SkipIfNotSupported(t, err)
54 if err != nil {
55 t.Fatal("Can't create link:", err)
56 }
57
58 // It's currently not possible for a program to replace
59 // itself.
60 prog2 := mustCgroupEgressProgram(t)
61 testLink(t, link, testLinkOptions{
62 prog: prog2,
63 })
64 }
65
66 func TestLinkCgroup(t *testing.T) {
67 cgroup, prog, cleanup := mustCgroupFixtures(t)
68 defer cleanup()
69
70 link, err := newLinkCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog)
71 testutils.SkipIfNotSupported(t, err)
72 if err != nil {
73 t.Fatal("Can't create link:", err)
74 }
75
76 testLink(t, link, testLinkOptions{
77 prog: prog,
78 loadPinned: LoadPinnedCgroup,
79 })
80 }
0 // Package link allows attaching eBPF programs to various kernel hooks.
1 package link
0 package link
1
2 import (
3 "fmt"
4 "io"
5
6 "github.com/cilium/ebpf"
7 )
8
9 type IterOptions struct {
10 // Program must be of type Tracing with attach type
11 // AttachTraceIter. The kind of iterator to attach to is
12 // determined at load time via the AttachTo field.
13 //
14 // AttachTo requires the kernel to include BTF of itself,
15 // and it to be compiled with a recent pahole (>= 1.16).
16 Program *ebpf.Program
17 }
18
19 // AttachIter attaches a BPF seq_file iterator.
20 func AttachIter(opts IterOptions) (*Iter, error) {
21 link, err := AttachRawLink(RawLinkOptions{
22 Program: opts.Program,
23 Attach: ebpf.AttachTraceIter,
24 })
25 if err != nil {
26 return nil, fmt.Errorf("can't link iterator: %w", err)
27 }
28
29 return &Iter{link}, err
30 }
31
32 // LoadPinnedIter loads a pinned iterator from a bpffs.
33 func LoadPinnedIter(fileName string) (*Iter, error) {
34 link, err := LoadPinnedRawLink(fileName)
35 if err != nil {
36 return nil, err
37 }
38
39 return &Iter{link}, err
40 }
41
42 // Iter represents an attached bpf_iter.
43 type Iter struct {
44 link *RawLink
45 }
46
47 var _ Link = (*Iter)(nil)
48
49 func (it *Iter) isLink() {}
50
51 // Close implements Link.
52 func (it *Iter) Close() error {
53 return it.link.Close()
54 }
55
56 // Pin implements Link.
57 func (it *Iter) Pin(fileName string) error {
58 return it.link.Pin(fileName)
59 }
60
61 // Update implements Link.
62 func (it *Iter) Update(new *ebpf.Program) error {
63 return it.link.Update(new)
64 }
65
66 // Open creates a new instance of the iterator.
67 //
68 // Reading from the returned reader triggers the BPF program.
69 func (it *Iter) Open() (io.ReadCloser, error) {
70 linkFd, err := it.link.fd.Value()
71 if err != nil {
72 return nil, err
73 }
74
75 attr := &bpfIterCreateAttr{
76 linkFd: linkFd,
77 }
78
79 fd, err := bpfIterCreate(attr)
80 if err != nil {
81 return nil, fmt.Errorf("can't create iterator: %w", err)
82 }
83
84 return fd.File("bpf_iter"), nil
85 }
0 package link
1
2 import (
3 "errors"
4 "io/ioutil"
5 "testing"
6
7 "github.com/cilium/ebpf"
8 "github.com/cilium/ebpf/asm"
9 "github.com/cilium/ebpf/internal/btf"
10 )
11
12 func TestIter(t *testing.T) {
13 prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
14 Type: ebpf.Tracing,
15 AttachType: ebpf.AttachTraceIter,
16 AttachTo: "bpf_map",
17 Instructions: asm.Instructions{
18 asm.Mov.Imm(asm.R0, 0),
19 asm.Return(),
20 },
21 License: "MIT",
22 })
23 if errors.Is(err, btf.ErrNotFound) {
24 t.Skip("Kernel doesn't support iter:", err)
25 }
26 if err != nil {
27 t.Fatal("Can't load program:", err)
28 }
29 defer prog.Close()
30
31 it, err := AttachIter(IterOptions{
32 Program: prog,
33 })
34 if err != nil {
35 t.Fatal("Can't create iter:", err)
36 }
37
38 file, err := it.Open()
39 if err != nil {
40 t.Fatal("Can't open iter instance:", err)
41 }
42 defer file.Close()
43
44 contents, err := ioutil.ReadAll(file)
45 if err != nil {
46 t.Fatal(err)
47 }
48
49 if len(contents) != 0 {
50 t.Error("Non-empty output from no-op iterator:", string(contents))
51 }
52
53 testLink(t, it, testLinkOptions{
54 prog: prog,
55 loadPinned: func(s string) (Link, error) {
56 return LoadPinnedIter(s)
57 },
58 })
59 }
0 package link
1
2 import (
3 "fmt"
4
5 "github.com/cilium/ebpf"
6 "github.com/cilium/ebpf/internal"
7 )
8
9 var ErrNotSupported = internal.ErrNotSupported
10
11 // Link represents a Program attached to a BPF hook.
12 type Link interface {
13 // Replace the current program with a new program.
14 //
15 // Passing a nil program is an error.
16 Update(*ebpf.Program) error
17
18 // Persist a link by pinning it into a bpffs.
19 //
20 // May return an error wrapping ErrNotSupported.
21 Pin(string) error
22
23 // Close frees resources.
24 //
25 // The link will be broken unless it has been pinned. A link
26 // may continue past the lifetime of the process if Close is
27 // not called.
28 Close() error
29
30 // Prevent external users from implementing this interface.
31 isLink()
32 }
33
34 // RawLinkOptions control the creation of a raw link.
35 type RawLinkOptions struct {
36 // File descriptor to attach to. This differs for each attach type.
37 Target int
38 // Program to attach.
39 Program *ebpf.Program
40 // Attach must match the attach type of Program.
41 Attach ebpf.AttachType
42 }
43
44 // RawLink is the low-level API to bpf_link.
45 //
46 // You should consider using the higher level interfaces in this
47 // package instead.
48 type RawLink struct {
49 fd *internal.FD
50 }
51
52 // AttachRawLink creates a raw link.
53 func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
54 if err := haveBPFLink(); err != nil {
55 return nil, err
56 }
57
58 if opts.Target < 0 {
59 return nil, fmt.Errorf("invalid target: %s", internal.ErrClosedFd)
60 }
61
62 progFd := opts.Program.FD()
63 if progFd < 0 {
64 return nil, fmt.Errorf("invalid program: %s", internal.ErrClosedFd)
65 }
66
67 attr := bpfLinkCreateAttr{
68 targetFd: uint32(opts.Target),
69 progFd: uint32(progFd),
70 attachType: opts.Attach,
71 }
72 fd, err := bpfLinkCreate(&attr)
73 if err != nil {
74 return nil, fmt.Errorf("can't create link: %s", err)
75 }
76
77 return &RawLink{fd}, nil
78 }
79
80 // LoadPinnedRawLink loads a persisted link from a bpffs.
81 func LoadPinnedRawLink(fileName string) (*RawLink, error) {
82 fd, err := internal.BPFObjGet(fileName)
83 if err != nil {
84 return nil, fmt.Errorf("can't load pinned link: %s", err)
85 }
86
87 return &RawLink{fd}, nil
88 }
89
90 func (l *RawLink) isLink() {}
91
92 // Close breaks the link.
93 //
94 // Use Pin if you want to make the link persistent.
95 func (l *RawLink) Close() error {
96 return l.fd.Close()
97 }
98
99 // Pin persists a link past the lifetime of the process.
100 //
101 // Calling Close on a pinned Link will not break the link
102 // until the pin is removed.
103 func (l *RawLink) Pin(fileName string) error {
104 if err := internal.BPFObjPin(fileName, l.fd); err != nil {
105 return fmt.Errorf("can't pin link: %s", err)
106 }
107 return nil
108 }
109
110 // Update implements Link.
111 func (l *RawLink) Update(new *ebpf.Program) error {
112 return l.UpdateArgs(RawLinkUpdateOptions{
113 New: new,
114 })
115 }
116
117 // RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs.
118 type RawLinkUpdateOptions struct {
119 New *ebpf.Program
120 Old *ebpf.Program
121 Flags uint32
122 }
123
124 // UpdateArgs updates a link based on args.
125 func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error {
126 newFd := opts.New.FD()
127 if newFd < 0 {
128 return fmt.Errorf("invalid program: %s", internal.ErrClosedFd)
129 }
130
131 var oldFd int
132 if opts.Old != nil {
133 oldFd = opts.Old.FD()
134 if oldFd < 0 {
135 return fmt.Errorf("invalid replacement program: %s", internal.ErrClosedFd)
136 }
137 }
138
139 linkFd, err := l.fd.Value()
140 if err != nil {
141 return fmt.Errorf("can't update link: %s", err)
142 }
143
144 attr := bpfLinkUpdateAttr{
145 linkFd: linkFd,
146 newProgFd: uint32(newFd),
147 oldProgFd: uint32(oldFd),
148 flags: opts.Flags,
149 }
150 return bpfLinkUpdate(&attr)
151 }
0 package link
1
2 import (
3 "errors"
4 "io/ioutil"
5 "os"
6 "path/filepath"
7 "reflect"
8 "testing"
9
10 "github.com/cilium/ebpf"
11 "github.com/cilium/ebpf/asm"
12 "github.com/cilium/ebpf/internal/testutils"
13 )
14
15 func TestRawLink(t *testing.T) {
16 cgroup, prog, cleanup := mustCgroupFixtures(t)
17 defer cleanup()
18
19 link, err := AttachRawLink(RawLinkOptions{
20 Target: int(cgroup.Fd()),
21 Program: prog,
22 Attach: ebpf.AttachCGroupInetEgress,
23 })
24 testutils.SkipIfNotSupported(t, err)
25 if err != nil {
26 t.Fatal("Can't create raw link:", err)
27 }
28
29 testLink(t, link, testLinkOptions{
30 prog: prog,
31 loadPinned: func(f string) (Link, error) {
32 return LoadPinnedRawLink(f)
33 },
34 })
35 }
36
37 func mustCgroupFixtures(t *testing.T) (*os.File, *ebpf.Program, func()) {
38 t.Helper()
39
40 testutils.SkipIfNotSupported(t, haveProgAttach())
41
42 prog := mustCgroupEgressProgram(t)
43 cgdir, err := ioutil.TempDir("/sys/fs/cgroup/unified", "ebpf-link")
44 if err != nil {
45 prog.Close()
46 t.Fatal("Can't create cgroupv2:", err)
47 }
48
49 cgroup, err := os.Open(cgdir)
50 if err != nil {
51 prog.Close()
52 os.Remove(cgdir)
53 t.Fatal(err)
54 }
55
56 return cgroup, prog, func() {
57 prog.Close()
58 cgroup.Close()
59 os.Remove(cgdir)
60 }
61 }
62
63 func mustCgroupEgressProgram(t *testing.T) *ebpf.Program {
64 t.Helper()
65
66 prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
67 Type: ebpf.CGroupSKB,
68 AttachType: ebpf.AttachCGroupInetEgress,
69 License: "MIT",
70 Instructions: asm.Instructions{
71 asm.Mov.Imm(asm.R0, 0),
72 asm.Return(),
73 },
74 })
75 if err != nil {
76 t.Fatal(err)
77 }
78 return prog
79 }
80
81 type testLinkOptions struct {
82 prog *ebpf.Program
83 loadPinned func(string) (Link, error)
84 }
85
86 func testLink(t *testing.T, link Link, opts testLinkOptions) {
87 t.Helper()
88
89 tmp, err := ioutil.TempDir("/sys/fs/bpf", "ebpf-test")
90 if err != nil {
91 t.Fatal(err)
92 }
93 defer os.RemoveAll(tmp)
94
95 path := filepath.Join(tmp, "link")
96 err = link.Pin(path)
97 if err == ErrNotSupported {
98 t.Errorf("%T.Pin returns unwrapped ErrNotSupported", link)
99 }
100
101 if opts.loadPinned == nil {
102 if !errors.Is(err, ErrNotSupported) {
103 t.Errorf("%T.Pin doesn't return ErrNotSupported: %s", link, err)
104 }
105 } else {
106 if err != nil {
107 t.Fatalf("Can't pin %T: %s", link, err)
108 }
109
110 link2, err := opts.loadPinned(path)
111 if err != nil {
112 t.Fatalf("Can't load pinned %T: %s", link, err)
113 }
114 link2.Close()
115
116 if reflect.TypeOf(link) != reflect.TypeOf(link2) {
117 t.Errorf("Loading a pinned %T returns a %T", link, link2)
118 }
119 }
120
121 if err := link.Update(opts.prog); err != nil {
122 t.Fatalf("%T.Update returns an error: %s", link, err)
123 }
124
125 func() {
126 // Panicking is OK
127 defer func() {
128 recover()
129 }()
130
131 if err := link.Update(nil); err == nil {
132 t.Fatalf("%T.Update accepts nil program", link)
133 }
134 }()
135
136 if err := link.Close(); err != nil {
137 t.Fatalf("%T.Close returns an error: %s", link, err)
138 }
139 }
0 package link
1
2 import (
3 "fmt"
4
5 "github.com/cilium/ebpf"
6 "github.com/cilium/ebpf/internal"
7 )
8
9 type RawAttachProgramOptions struct {
10 // File descriptor to attach to. This differs for each attach type.
11 Target int
12 // Program to attach.
13 Program *ebpf.Program
14 // Program to replace (cgroups).
15 Replace *ebpf.Program
16 // Attach must match the attach type of Program (and Replace).
17 Attach ebpf.AttachType
18 // Flags control the attach behaviour. This differs for each attach type.
19 Flags uint32
20 }
21
22 // RawAttachProgram is a low level wrapper around BPF_PROG_ATTACH.
23 //
24 // You should use one of the higher level abstractions available in this
25 // package if possible.
26 func RawAttachProgram(opts RawAttachProgramOptions) error {
27 if err := haveProgAttach(); err != nil {
28 return err
29 }
30
31 var replaceFd uint32
32 if opts.Replace != nil {
33 replaceFd = uint32(opts.Replace.FD())
34 }
35
36 attr := internal.BPFProgAttachAttr{
37 TargetFd: uint32(opts.Target),
38 AttachBpfFd: uint32(opts.Program.FD()),
39 ReplaceBpfFd: replaceFd,
40 AttachType: uint32(opts.Attach),
41 AttachFlags: uint32(opts.Flags),
42 }
43
44 if err := internal.BPFProgAttach(&attr); err != nil {
45 return fmt.Errorf("can't attach program: %s", err)
46 }
47 return nil
48 }
49
50 type RawDetachProgramOptions struct {
51 Target int
52 Program *ebpf.Program
53 Attach ebpf.AttachType
54 }
55
56 // RawDetachProgram is a low level wrapper around BPF_PROG_DETACH.
57 //
58 // You should use one of the higher level abstractions available in this
59 // package if possible.
60 func RawDetachProgram(opts RawDetachProgramOptions) error {
61 if err := haveProgAttach(); err != nil {
62 return err
63 }
64
65 attr := internal.BPFProgDetachAttr{
66 TargetFd: uint32(opts.Target),
67 AttachBpfFd: uint32(opts.Program.FD()),
68 AttachType: uint32(opts.Attach),
69 }
70 if err := internal.BPFProgDetach(&attr); err != nil {
71 return fmt.Errorf("can't detach program: %s", err)
72 }
73
74 return nil
75 }
0 package link
1
2 import (
3 "testing"
4
5 "github.com/cilium/ebpf"
6 "github.com/cilium/ebpf/asm"
7 "github.com/cilium/ebpf/internal/testutils"
8 )
9
10 func TestProgramAlter(t *testing.T) {
11 testutils.SkipOnOldKernel(t, "4.13", "SkSKB type")
12
13 var err error
14 var prog *ebpf.Program
15 prog, err = ebpf.NewProgram(&ebpf.ProgramSpec{
16 Type: ebpf.SkSKB,
17 Instructions: asm.Instructions{
18 asm.LoadImm(asm.R0, 0, asm.DWord),
19 asm.Return(),
20 },
21 License: "MIT",
22 })
23 if err != nil {
24 t.Fatal(err)
25 }
26 defer prog.Close()
27
28 var sockMap *ebpf.Map
29 sockMap, err = ebpf.NewMap(&ebpf.MapSpec{
30 Type: ebpf.MapType(15), // BPF_MAP_TYPE_SOCKMAP
31 KeySize: 4,
32 ValueSize: 4,
33 MaxEntries: 2,
34 })
35 if err != nil {
36 t.Fatal(err)
37 }
38 defer sockMap.Close()
39
40 err = RawAttachProgram(RawAttachProgramOptions{
41 Target: sockMap.FD(),
42 Program: prog,
43 Attach: ebpf.AttachSkSKBStreamParser,
44 })
45 if err != nil {
46 t.Fatal(err)
47 }
48
49 err = RawDetachProgram(RawDetachProgramOptions{
50 Target: sockMap.FD(),
51 Program: prog,
52 Attach: ebpf.AttachSkSKBStreamParser,
53 })
54 if err != nil {
55 t.Fatal(err)
56 }
57 }
0 package link
1
2 import (
3 "fmt"
4
5 "github.com/cilium/ebpf"
6 "github.com/cilium/ebpf/internal"
7 )
8
9 type RawTracepointOptions struct {
10 // Tracepoint name.
11 Name string
12 // Program must be of type RawTracepoint*
13 Program *ebpf.Program
14 }
15
16 // AttachRawTracepoint links a BPF program to a raw_tracepoint.
17 //
18 // Requires at least Linux 4.17.
19 func AttachRawTracepoint(opts RawTracepointOptions) (Link, error) {
20 if opts.Program.FD() < 0 {
21 return nil, fmt.Errorf("invalid program: %w", internal.ErrClosedFd)
22 }
23
24 fd, err := bpfRawTracepointOpen(&bpfRawTracepointOpenAttr{
25 name: internal.NewStringPointer(opts.Name),
26 fd: uint32(opts.Program.FD()),
27 })
28 if err != nil {
29 return nil, err
30 }
31
32 return &progAttachRawTracepoint{fd: fd}, nil
33 }
34
35 type progAttachRawTracepoint struct {
36 fd *internal.FD
37 }
38
39 var _ Link = (*progAttachRawTracepoint)(nil)
40
41 func (rt *progAttachRawTracepoint) isLink() {}
42
43 func (rt *progAttachRawTracepoint) Close() error {
44 return rt.fd.Close()
45 }
46
47 func (rt *progAttachRawTracepoint) Update(_ *ebpf.Program) error {
48 return fmt.Errorf("can't update raw_tracepoint: %w", ErrNotSupported)
49 }
50
51 func (rt *progAttachRawTracepoint) Pin(_ string) error {
52 return fmt.Errorf("can't pin raw_tracepoint: %w", ErrNotSupported)
53 }
0 package link
1
2 import (
3 "testing"
4
5 "github.com/cilium/ebpf"
6 "github.com/cilium/ebpf/asm"
7 "github.com/cilium/ebpf/internal/testutils"
8 )
9
10 func TestRawTracepoint(t *testing.T) {
11 testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API")
12
13 prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
14 Type: ebpf.RawTracepoint,
15 AttachType: ebpf.AttachNone,
16 Instructions: asm.Instructions{
17 asm.LoadImm(asm.R0, 0, asm.DWord),
18 asm.Return(),
19 },
20 License: "GPL",
21 })
22 if err != nil {
23 t.Fatal(err)
24 }
25 defer prog.Close()
26
27 link, err := AttachRawTracepoint(RawTracepointOptions{
28 Name: "cgroup_mkdir",
29 Program: prog,
30 })
31 if err != nil {
32 t.Fatal(err)
33 }
34
35 if link == nil {
36 t.Fatal("no link")
37 }
38
39 if err := link.Close(); err != nil {
40 t.Fatalf("failed to close link: %v", err)
41 }
42 }
43
44 func TestRawTracepoint_writable(t *testing.T) {
45 testutils.SkipOnOldKernel(t, "5.2", "BPF_RAW_TRACEPOINT_WRITABLE API")
46
47 prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
48 Type: ebpf.RawTracepointWritable,
49 AttachType: ebpf.AttachNone,
50 Instructions: asm.Instructions{
51 asm.LoadImm(asm.R0, 0, asm.DWord),
52 asm.Return(),
53 },
54 License: "GPL",
55 })
56 if err != nil {
57 t.Fatal(err)
58 }
59 defer prog.Close()
60
61 link, err := AttachRawTracepoint(RawTracepointOptions{
62 Name: "cgroup_rmdir",
63 Program: prog,
64 })
65 if err != nil {
66 t.Fatal(err)
67 }
68
69 if link == nil {
70 t.Fatal("no link")
71 }
72
73 if err := link.Close(); err != nil {
74 t.Fatalf("failed to close link: %v", err)
75 }
76 }
0 package link
1
2 import (
3 "errors"
4 "unsafe"
5
6 "github.com/cilium/ebpf"
7 "github.com/cilium/ebpf/asm"
8 "github.com/cilium/ebpf/internal"
9 "github.com/cilium/ebpf/internal/unix"
10 )
11
12 var haveProgAttach = internal.FeatureTest("BPF_PROG_ATTACH", "4.10", func() (bool, error) {
13 prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
14 Type: ebpf.CGroupSKB,
15 AttachType: ebpf.AttachCGroupInetIngress,
16 License: "MIT",
17 Instructions: asm.Instructions{
18 asm.Mov.Imm(asm.R0, 0),
19 asm.Return(),
20 },
21 })
22 if err != nil {
23 return false, nil
24 }
25
26 // BPF_PROG_ATTACH was introduced at the same time as CGgroupSKB,
27 // so being able to load the program is enough to infer that we
28 // have the syscall.
29 prog.Close()
30 return true, nil
31 })
32
33 var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replacement", "5.5", func() (bool, error) {
34 if err := haveProgAttach(); err != nil {
35 return false, err
36 }
37
38 prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
39 Type: ebpf.CGroupSKB,
40 AttachType: ebpf.AttachCGroupInetIngress,
41 License: "MIT",
42 Instructions: asm.Instructions{
43 asm.Mov.Imm(asm.R0, 0),
44 asm.Return(),
45 },
46 })
47 if err != nil {
48 return false, nil
49 }
50 defer prog.Close()
51
52 // We know that we have BPF_PROG_ATTACH since we can load CGroupSKB programs.
53 // If passing BPF_F_REPLACE gives us EINVAL we know that the feature isn't
54 // present.
55 attr := internal.BPFProgAttachAttr{
56 // We rely on this being checked after attachFlags.
57 TargetFd: ^uint32(0),
58 AttachBpfFd: uint32(prog.FD()),
59 AttachType: uint32(ebpf.AttachCGroupInetIngress),
60 AttachFlags: uint32(flagReplace),
61 }
62
63 err = internal.BPFProgAttach(&attr)
64 if errors.Is(err, unix.EPERM) {
65 // We don't have enough permissions, so we never get to the point
66 // where flags are checked.
67 return false, err
68 }
69 return !errors.Is(err, unix.EINVAL), nil
70 })
71
72 type bpfLinkCreateAttr struct {
73 progFd uint32
74 targetFd uint32
75 attachType ebpf.AttachType
76 flags uint32
77 }
78
79 func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) {
80 ptr, err := internal.BPF(internal.BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
81 if err != nil {
82 return nil, err
83 }
84 return internal.NewFD(uint32(ptr)), nil
85 }
86
87 type bpfLinkUpdateAttr struct {
88 linkFd uint32
89 newProgFd uint32
90 flags uint32
91 oldProgFd uint32
92 }
93
94 func bpfLinkUpdate(attr *bpfLinkUpdateAttr) error {
95 _, err := internal.BPF(internal.BPF_LINK_UPDATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
96 return err
97 }
98
99 var haveBPFLink = internal.FeatureTest("bpf_link", "5.7", func() (bool, error) {
100 prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
101 Type: ebpf.CGroupSKB,
102 AttachType: ebpf.AttachCGroupInetIngress,
103 License: "MIT",
104 Instructions: asm.Instructions{
105 asm.Mov.Imm(asm.R0, 0),
106 asm.Return(),
107 },
108 })
109 if err != nil {
110 return false, nil
111 }
112 defer prog.Close()
113
114 attr := bpfLinkCreateAttr{
115 // This is a hopefully invalid file descriptor, which triggers EBADF.
116 targetFd: ^uint32(0),
117 progFd: uint32(prog.FD()),
118 attachType: ebpf.AttachCGroupInetIngress,
119 }
120 _, err = bpfLinkCreate(&attr)
121 return !errors.Is(err, unix.EINVAL), nil
122 })
123
124 type bpfIterCreateAttr struct {
125 linkFd uint32
126 flags uint32
127 }
128
129 func bpfIterCreate(attr *bpfIterCreateAttr) (*internal.FD, error) {
130 ptr, err := internal.BPF(internal.BPF_ITER_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
131 if err == nil {
132 return internal.NewFD(uint32(ptr)), nil
133 }
134 return nil, err
135 }
136
137 type bpfRawTracepointOpenAttr struct {
138 name internal.Pointer
139 fd uint32
140 _ uint32
141 }
142
143 func bpfRawTracepointOpen(attr *bpfRawTracepointOpenAttr) (*internal.FD, error) {
144 ptr, err := internal.BPF(internal.BPF_RAW_TRACEPOINT_OPEN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
145 if err == nil {
146 return internal.NewFD(uint32(ptr)), nil
147 }
148 return nil, err
149 }
0 package link
1
2 import (
3 "testing"
4
5 "github.com/cilium/ebpf/internal/testutils"
6 )
7
8 func TestHaveProgAttach(t *testing.T) {
9 testutils.CheckFeatureTest(t, haveProgAttach)
10 }
11
12 func TestHaveProgAttachReplace(t *testing.T) {
13 testutils.CheckFeatureTest(t, haveProgAttachReplace)
14 }
15
16 func TestHaveBPFLink(t *testing.T) {
17 testutils.CheckFeatureTest(t, haveBPFLink)
18 }
00 package ebpf
11
22 import (
3 "fmt"
4
35 "github.com/cilium/ebpf/asm"
46 "github.com/cilium/ebpf/internal/btf"
5
6 "golang.org/x/xerrors"
77 )
88
99 // link resolves bpf-to-bpf calls.
2727
2828 needed, err := needSection(insns, lib.Instructions)
2929 if err != nil {
30 return xerrors.Errorf("linking %s: %w", lib.Name, err)
30 return fmt.Errorf("linking %s: %w", lib.Name, err)
3131 }
3232
3333 if !needed {
4040
4141 if prog.BTF != nil && lib.BTF != nil {
4242 if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil {
43 return xerrors.Errorf("linking BTF of %s: %w", lib.Name, err)
43 return fmt.Errorf("linking BTF of %s: %w", lib.Name, err)
4444 }
4545 }
4646 }
00 package ebpf
11
22 import (
3 "errors"
34 "fmt"
45 "strings"
56
67 "github.com/cilium/ebpf/internal"
78 "github.com/cilium/ebpf/internal/btf"
89 "github.com/cilium/ebpf/internal/unix"
9
10 "golang.org/x/xerrors"
1110 )
1211
1312 // Errors returned by Map and MapIterator methods.
1413 var (
15 ErrKeyNotExist = xerrors.New("key does not exist")
16 ErrIterationAborted = xerrors.New("iteration aborted")
14 ErrKeyNotExist = errors.New("key does not exist")
15 ErrKeyExist = errors.New("key already exists")
16 ErrIterationAborted = errors.New("iteration aborted")
1717 )
1818
1919 // MapID represents the unique ID of an eBPF map
9090 // You should not use fd after calling this function.
9191 func NewMapFromFD(fd int) (*Map, error) {
9292 if fd < 0 {
93 return nil, xerrors.New("invalid fd")
93 return nil, errors.New("invalid fd")
9494 }
9595 bpfFd := internal.NewFD(uint32(fd))
9696
106106 //
107107 // Creating a map for the first time will perform feature detection
108108 // by creating small, temporary maps.
109 //
110 // The caller is responsible for ensuring the process' rlimit is set
111 // sufficiently high for locking memory during map creation. This can be done
112 // by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMap.
109113 func NewMap(spec *MapSpec) (*Map, error) {
110114 if spec.BTF == nil {
111115 return newMapWithBTF(spec, nil)
112116 }
113117
114118 handle, err := btf.NewHandle(btf.MapSpec(spec.BTF))
115 if err != nil && !xerrors.Is(err, btf.ErrNotSupported) {
116 return nil, xerrors.Errorf("can't load BTF: %w", err)
119 if err != nil && !errors.Is(err, btf.ErrNotSupported) {
120 return nil, fmt.Errorf("can't load BTF: %w", err)
117121 }
118122
119123 return newMapWithBTF(spec, handle)
125129 }
126130
127131 if spec.InnerMap == nil {
128 return nil, xerrors.Errorf("%s requires InnerMap", spec.Type)
132 return nil, fmt.Errorf("%s requires InnerMap", spec.Type)
129133 }
130134
131135 template, err := createMap(spec.InnerMap, nil, handle)
149153 }
150154
151155 if abi.ValueSize != 0 && abi.ValueSize != 4 {
152 return nil, xerrors.New("ValueSize must be zero or four for map of map")
156 return nil, errors.New("ValueSize must be zero or four for map of map")
153157 }
154158 abi.ValueSize = 4
155159
156160 case PerfEventArray:
157161 if abi.KeySize != 0 && abi.KeySize != 4 {
158 return nil, xerrors.New("KeySize must be zero or four for perf event array")
162 return nil, errors.New("KeySize must be zero or four for perf event array")
159163 }
160164 abi.KeySize = 4
161165
162166 if abi.ValueSize != 0 && abi.ValueSize != 4 {
163 return nil, xerrors.New("ValueSize must be zero or four for perf event array")
167 return nil, errors.New("ValueSize must be zero or four for perf event array")
164168 }
165169 abi.ValueSize = 4
166170
167171 if abi.MaxEntries == 0 {
168172 n, err := internal.PossibleCPUs()
169173 if err != nil {
170 return nil, xerrors.Errorf("perf event array: %w", err)
174 return nil, fmt.Errorf("perf event array: %w", err)
171175 }
172176 abi.MaxEntries = uint32(n)
173177 }
175179
176180 if abi.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze {
177181 if err := haveMapMutabilityModifiers(); err != nil {
178 return nil, xerrors.Errorf("map create: %w", err)
182 return nil, fmt.Errorf("map create: %w", err)
179183 }
180184 }
181185
191195 var err error
192196 attr.innerMapFd, err = inner.Value()
193197 if err != nil {
194 return nil, xerrors.Errorf("map create: %w", err)
198 return nil, fmt.Errorf("map create: %w", err)
195199 }
196200 }
197201
207211
208212 fd, err := bpfMapCreate(&attr)
209213 if err != nil {
210 return nil, xerrors.Errorf("map create: %w", err)
214 return nil, fmt.Errorf("map create: %w", err)
211215 }
212216
213217 m, err := newMap(fd, spec.Name, abi)
217221
218222 if err := m.populate(spec.Contents); err != nil {
219223 m.Close()
220 return nil, xerrors.Errorf("map create: can't set initial contents: %w", err)
224 return nil, fmt.Errorf("map create: can't set initial contents: %w", err)
221225 }
222226
223227 if spec.Freeze {
224228 if err := m.Freeze(); err != nil {
225229 m.Close()
226 return nil, xerrors.Errorf("can't freeze map: %w", err)
230 return nil, fmt.Errorf("can't freeze map: %w", err)
227231 }
228232 }
229233
295299 *value = m
296300 return nil
297301 case *Map:
298 return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
302 return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
299303 case Map:
300 return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
304 return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
301305
302306 case **Program:
303307 p, err := unmarshalProgram(valueBytes)
309313 *value = p
310314 return nil
311315 case *Program:
312 return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
316 return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
313317 case Program:
314 return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
318 return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
315319
316320 default:
317321 return unmarshalBytes(valueOut, valueBytes)
326330
327331 keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
328332 if err != nil {
329 return xerrors.Errorf("can't marshal key: %w", err)
333 return fmt.Errorf("can't marshal key: %w", err)
330334 }
331335
332336 if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil {
333 return xerrors.Errorf("lookup and delete failed: %w", err)
337 return fmt.Errorf("lookup and delete failed: %w", err)
334338 }
335339
336340 return unmarshalBytes(valueOut, valueBytes)
344348 valuePtr := internal.NewSlicePointer(valueBytes)
345349
346350 err := m.lookup(key, valuePtr)
347 if xerrors.Is(err, ErrKeyNotExist) {
351 if errors.Is(err, ErrKeyNotExist) {
348352 return nil, nil
349353 }
350354
354358 func (m *Map) lookup(key interface{}, valueOut internal.Pointer) error {
355359 keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
356360 if err != nil {
357 return xerrors.Errorf("can't marshal key: %w", err)
361 return fmt.Errorf("can't marshal key: %w", err)
358362 }
359363
360364 if err = bpfMapLookupElem(m.fd, keyPtr, valueOut); err != nil {
361 return xerrors.Errorf("lookup failed: %w", err)
365 return fmt.Errorf("lookup failed: %w", err)
362366 }
363367 return nil
364368 }
388392 func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error {
389393 keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
390394 if err != nil {
391 return xerrors.Errorf("can't marshal key: %w", err)
395 return fmt.Errorf("can't marshal key: %w", err)
392396 }
393397
394398 var valuePtr internal.Pointer
398402 valuePtr, err = marshalPtr(value, int(m.abi.ValueSize))
399403 }
400404 if err != nil {
401 return xerrors.Errorf("can't marshal value: %w", err)
405 return fmt.Errorf("can't marshal value: %w", err)
402406 }
403407
404408 if err = bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags)); err != nil {
405 return xerrors.Errorf("update failed: %w", err)
409 return fmt.Errorf("update failed: %w", err)
406410 }
407411
408412 return nil
414418 func (m *Map) Delete(key interface{}) error {
415419 keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
416420 if err != nil {
417 return xerrors.Errorf("can't marshal key: %w", err)
421 return fmt.Errorf("can't marshal key: %w", err)
418422 }
419423
420424 if err = bpfMapDeleteElem(m.fd, keyPtr); err != nil {
421 return xerrors.Errorf("delete failed: %w", err)
425 return fmt.Errorf("delete failed: %w", err)
422426 }
423427 return nil
424428 }
440444 }
441445
442446 if err := unmarshalBytes(nextKeyOut, nextKeyBytes); err != nil {
443 return xerrors.Errorf("can't unmarshal next key: %w", err)
447 return fmt.Errorf("can't unmarshal next key: %w", err)
444448 }
445449 return nil
446450 }
457461 nextKeyPtr := internal.NewSlicePointer(nextKey)
458462
459463 err := m.nextKey(key, nextKeyPtr)
460 if xerrors.Is(err, ErrKeyNotExist) {
464 if errors.Is(err, ErrKeyNotExist) {
461465 return nil, nil
462466 }
463467
473477 if key != nil {
474478 keyPtr, err = marshalPtr(key, int(m.abi.KeySize))
475479 if err != nil {
476 return xerrors.Errorf("can't marshal key: %w", err)
480 return fmt.Errorf("can't marshal key: %w", err)
477481 }
478482 }
479483
480484 if err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut); err != nil {
481 return xerrors.Errorf("next key failed: %w", err)
485 return fmt.Errorf("next key failed: %w", err)
482486 }
483487 return nil
484488 }
531535
532536 dup, err := m.fd.Dup()
533537 if err != nil {
534 return nil, xerrors.Errorf("can't clone map: %w", err)
538 return nil, fmt.Errorf("can't clone map: %w", err)
535539 }
536540
537541 return newMap(dup, m.name, &m.abi)
541545 //
542546 // This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional
543547 func (m *Map) Pin(fileName string) error {
544 return bpfPinObject(fileName, m.fd)
548 return internal.BPFObjPin(fileName, m.fd)
545549 }
546550
547551 // Freeze prevents a map to be modified from user space.
549553 // It makes no changes to kernel-side restrictions.
550554 func (m *Map) Freeze() error {
551555 if err := haveMapMutabilityModifiers(); err != nil {
552 return xerrors.Errorf("can't freeze map: %w", err)
556 return fmt.Errorf("can't freeze map: %w", err)
553557 }
554558
555559 if err := bpfMapFreeze(m.fd); err != nil {
556 return xerrors.Errorf("can't freeze map: %w", err)
560 return fmt.Errorf("can't freeze map: %w", err)
557561 }
558562 return nil
559563 }
561565 func (m *Map) populate(contents []MapKV) error {
562566 for _, kv := range contents {
563567 if err := m.Put(kv.Key, kv.Value); err != nil {
564 return xerrors.Errorf("key %v: %w", kv.Key, err)
568 return fmt.Errorf("key %v: %w", kv.Key, err)
565569 }
566570 }
567571 return nil
572576 // The function is not compatible with nested maps.
573577 // Use LoadPinnedMapExplicit in these situations.
574578 func LoadPinnedMap(fileName string) (*Map, error) {
575 fd, err := bpfGetObject(fileName)
579 fd, err := internal.BPFObjGet(fileName)
576580 if err != nil {
577581 return nil, err
578582 }
586590
587591 // LoadPinnedMapExplicit loads a map with explicit parameters.
588592 func LoadPinnedMapExplicit(fileName string, abi *MapABI) (*Map, error) {
589 fd, err := bpfGetObject(fileName)
593 fd, err := internal.BPFObjGet(fileName)
590594 if err != nil {
591595 return nil, err
592596 }
595599
596600 func unmarshalMap(buf []byte) (*Map, error) {
597601 if len(buf) != 4 {
598 return nil, xerrors.New("map id requires 4 byte value")
602 return nil, errors.New("map id requires 4 byte value")
599603 }
600604
601605 // Looking up an entry in a nested map or prog array returns an id,
620624 replaced := make(map[string]bool)
621625 replace := func(name string, offset, size int, replacement interface{}) error {
622626 if offset+size > len(value) {
623 return xerrors.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size)
627 return fmt.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size)
624628 }
625629
626630 buf, err := marshalBytes(replacement, size)
627631 if err != nil {
628 return xerrors.Errorf("marshal %s: %w", name, err)
632 return fmt.Errorf("marshal %s: %w", name, err)
629633 }
630634
631635 copy(value[offset:offset+size], buf)
649653 }
650654
651655 default:
652 return xerrors.Errorf("patching %T is not supported", typ)
656 return fmt.Errorf("patching %T is not supported", typ)
653657 }
654658
655659 if len(replaced) == len(replacements) {
664668 }
665669
666670 if len(missing) == 1 {
667 return xerrors.Errorf("unknown field: %s", missing[0])
668 }
669
670 return xerrors.Errorf("unknown fields: %s", strings.Join(missing, ","))
671 return fmt.Errorf("unknown field: %s", missing[0])
672 }
673
674 return fmt.Errorf("unknown fields: %s", strings.Join(missing, ","))
671675 }
672676
673677 // MapIterator iterates a Map.
725729 mi.prevKey = mi.prevBytes
726730
727731 mi.err = mi.target.Lookup(nextBytes, valueOut)
728 if xerrors.Is(mi.err, ErrKeyNotExist) {
732 if errors.Is(mi.err, ErrKeyNotExist) {
729733 // Even though the key should be valid, we couldn't look up
730734 // its value. If we're iterating a hash map this is probably
731735 // because a concurrent delete removed the value before we
744748 return mi.err == nil
745749 }
746750
747 mi.err = xerrors.Errorf("%w", ErrIterationAborted)
751 mi.err = fmt.Errorf("%w", ErrIterationAborted)
748752 return false
749753 }
750754
761765 //
762766 // Returns ErrNotExist, if there is no next eBPF map.
763767 func MapGetNextID(startID MapID) (MapID, error) {
764 id, err := objGetNextID(_MapGetNextID, uint32(startID))
768 id, err := objGetNextID(internal.BPF_MAP_GET_NEXT_ID, uint32(startID))
765769 return MapID(id), err
766770 }
767771
769773 //
770774 // Returns ErrNotExist, if there is no eBPF map with the given id.
771775 func NewMapFromID(id MapID) (*Map, error) {
772 fd, err := bpfObjGetFDByID(_MapGetFDByID, uint32(id))
776 fd, err := bpfObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id))
773777 if err != nil {
774778 return nil, err
775779 }
00 package ebpf
11
22 import (
3 "errors"
34 "fmt"
45 "io/ioutil"
56 "math"
1314 "github.com/cilium/ebpf/internal"
1415 "github.com/cilium/ebpf/internal/testutils"
1516 "github.com/cilium/ebpf/internal/unix"
16
17 "golang.org/x/xerrors"
1817 )
1918
2019 func TestMain(m *testing.M) {
21 err := unix.Setrlimit(8, &unix.Rlimit{
22 Cur: math.MaxUint64,
23 Max: math.MaxUint64,
20 err := unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{
21 Cur: unix.RLIM_INFINITY,
22 Max: unix.RLIM_INFINITY,
2423 })
2524 if err != nil {
2625 fmt.Println("WARNING: Failed to adjust rlimit, tests may fail")
7473 t.Fatal("Can't close map:", err)
7574 }
7675
77 if err := m.Put(uint32(0), uint32(42)); !xerrors.Is(err, internal.ErrClosedFd) {
76 if err := m.Put(uint32(0), uint32(42)); !errors.Is(err, internal.ErrClosedFd) {
7877 t.Fatal("Put doesn't check for closed fd", err)
7978 }
8079
81 if _, err := m.LookupBytes(uint32(0)); !xerrors.Is(err, internal.ErrClosedFd) {
80 if _, err := m.LookupBytes(uint32(0)); !errors.Is(err, internal.ErrClosedFd) {
8281 t.Fatal("Get doesn't check for closed fd", err)
8382 }
8483 }
200199 t.Error("Want value 4242, got", v)
201200 }
202201
203 if err := m.LookupAndDelete(nil, &v); !xerrors.Is(err, ErrKeyNotExist) {
202 if err := m.LookupAndDelete(nil, &v); !errors.Is(err, ErrKeyNotExist) {
204203 t.Fatal("Lookup and delete on empty Queue:", err)
205204 }
206205 }
434433
435434 var tmp uint32
436435 err := hash.Lookup("hello", &tmp)
437 if !xerrors.Is(err, ErrKeyNotExist) {
436 if !errors.Is(err, ErrKeyNotExist) {
438437 t.Error("Lookup doesn't return ErrKeyNotExist")
439438 }
440439
446445 t.Error("LookupBytes returns non-nil buffer for non-existent key")
447446 }
448447
449 if err := hash.Delete("hello"); !xerrors.Is(err, ErrKeyNotExist) {
448 if err := hash.Delete("hello"); !errors.Is(err, ErrKeyNotExist) {
450449 t.Error("Deleting unknown key doesn't return ErrKeyNotExist")
451450 }
452451
453 if err := hash.NextKey(nil, &tmp); !xerrors.Is(err, ErrKeyNotExist) {
452 if err := hash.NextKey(nil, &tmp); !errors.Is(err, ErrKeyNotExist) {
454453 t.Error("Looking up next key in empty map doesn't return a non-existing error")
454 }
455 }
456
457 func TestExist(t *testing.T) {
458 hash := createHash()
459 defer hash.Close()
460
461 if err := hash.Put("hello", uint32(21)); err != nil {
462 t.Errorf("Failed to put key/value pair into hash: %v", err)
463 }
464
465 if err := hash.Update("hello", uint32(42), UpdateNoExist); !errors.Is(err, ErrKeyExist) {
466 t.Error("Updating existing key doesn't return ErrKeyExist")
455467 }
456468 }
457469
489501 }
490502
491503 func TestPerCPUMarshaling(t *testing.T) {
492 numCPU, err := internal.PossibleCPUs()
493 if err != nil {
494 t.Fatal(err)
495 }
496 if numCPU < 2 {
497 t.Skip("Test requires at least two CPUs")
498 }
499
500 arr, err := NewMap(&MapSpec{
501 Type: PerCPUArray,
502 KeySize: 4,
503 ValueSize: 5,
504 MaxEntries: 1,
505 })
506 if err != nil {
507 t.Fatal(err)
508 }
509 defer arr.Close()
510
511 values := []*customEncoding{
512 &customEncoding{"hello"},
513 &customEncoding{"world"},
514 }
515 if err := arr.Put(uint32(0), values); err != nil {
516 t.Fatal(err)
517 }
518
519 // Make sure unmarshaling works on slices containing pointers
520 var retrieved []*customEncoding
521 if err := arr.Lookup(uint32(0), &retrieved); err != nil {
522 t.Fatal("Can't retrieve key 0:", err)
523 }
524
525 for i, want := range []string{"HELLO", "WORLD"} {
526 if retrieved[i] == nil {
527 t.Error("First item is nil")
528 } else if have := retrieved[i].data; have != want {
529 t.Errorf("Put doesn't use BinaryMarshaler, expected %s but got %s", want, have)
530 }
504 for _, typ := range []MapType{PerCPUHash, PerCPUArray, LRUCPUHash} {
505 t.Run(typ.String(), func(t *testing.T) {
506 numCPU, err := internal.PossibleCPUs()
507 if err != nil {
508 t.Fatal(err)
509 }
510 if numCPU < 2 {
511 t.Skip("Test requires at least two CPUs")
512 }
513 if typ == LRUCPUHash {
514 testutils.SkipOnOldKernel(t, "4.10", "LRU per-CPU hash")
515 }
516
517 arr, err := NewMap(&MapSpec{
518 Type: typ,
519 KeySize: 4,
520 ValueSize: 5,
521 MaxEntries: 1,
522 })
523 if err != nil {
524 t.Fatal(err)
525 }
526 defer arr.Close()
527
528 values := []*customEncoding{
529 {"hello"},
530 {"world"},
531 }
532 if err := arr.Put(uint32(0), values); err != nil {
533 t.Fatal(err)
534 }
535
536 // Make sure unmarshaling works on slices containing pointers
537 var retrieved []*customEncoding
538 if err := arr.Lookup(uint32(0), &retrieved); err != nil {
539 t.Fatal("Can't retrieve key 0:", err)
540 }
541
542 for i, want := range []string{"HELLO", "WORLD"} {
543 if retrieved[i] == nil {
544 t.Error("First item is nil")
545 } else if have := retrieved[i].data; have != want {
546 t.Errorf("Put doesn't use BinaryMarshaler, expected %s but got %s", want, have)
547 }
548 }
549
550 })
531551 }
532552 }
533553
709729 for {
710730 last := next
711731 if next, err = MapGetNextID(last); err != nil {
712 if !xerrors.Is(err, ErrNotExist) {
732 if !errors.Is(err, ErrNotExist) {
713733 t.Fatal("Expected ErrNotExist, got:", err)
714734 }
715735 break
739759
740760 // As there can be multiple maps, we use max(uint32) as MapID to trigger an expected error.
741761 _, err = NewMapFromID(MapID(math.MaxUint32))
742 if !xerrors.Is(err, ErrNotExist) {
762 if !errors.Is(err, ErrNotExist) {
743763 t.Fatal("Expected ErrNotExist, got:", err)
744764 }
745765 }
898918
899919 for i := 0; i < b.N; i++ {
900920 err := m.Delete(unsafe.Pointer(&key))
901 if err != nil && !xerrors.Is(err, ErrKeyNotExist) {
921 if err != nil && !errors.Is(err, ErrKeyNotExist) {
902922 b.Fatal(err)
903923 }
904924 }
33 "bytes"
44 "encoding"
55 "encoding/binary"
6 "errors"
7 "fmt"
68 "reflect"
79 "runtime"
810 "unsafe"
911
1012 "github.com/cilium/ebpf/internal"
11
12 "golang.org/x/xerrors"
1313 )
1414
1515 func marshalPtr(data interface{}, length int) (internal.Pointer, error) {
1717 if length == 0 {
1818 return internal.NewPointer(nil), nil
1919 }
20 return internal.Pointer{}, xerrors.New("can't use nil as key of map")
20 return internal.Pointer{}, errors.New("can't use nil as key of map")
2121 }
2222
2323 if ptr, ok := data.(unsafe.Pointer); ok {
4141 case []byte:
4242 buf = value
4343 case unsafe.Pointer:
44 err = xerrors.New("can't marshal from unsafe.Pointer")
44 err = errors.New("can't marshal from unsafe.Pointer")
4545 default:
4646 var wr bytes.Buffer
4747 err = binary.Write(&wr, internal.NativeEndian, value)
4848 if err != nil {
49 err = xerrors.Errorf("encoding %T: %v", value, err)
49 err = fmt.Errorf("encoding %T: %v", value, err)
5050 }
5151 buf = wr.Bytes()
5252 }
5555 }
5656
5757 if len(buf) != length {
58 return nil, xerrors.Errorf("%T doesn't marshal to %d bytes", data, length)
58 return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length)
5959 }
6060 return buf, nil
6161 }
9191 *value = buf
9292 return nil
9393 case string:
94 return xerrors.New("require pointer to string")
94 return errors.New("require pointer to string")
9595 case []byte:
96 return xerrors.New("require pointer to []byte")
96 return errors.New("require pointer to []byte")
9797 default:
9898 rd := bytes.NewReader(buf)
9999 if err := binary.Read(rd, internal.NativeEndian, value); err != nil {
100 return xerrors.Errorf("decoding %T: %v", value, err)
100 return fmt.Errorf("decoding %T: %v", value, err)
101101 }
102102 return nil
103103 }
112112 func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, error) {
113113 sliceType := reflect.TypeOf(slice)
114114 if sliceType.Kind() != reflect.Slice {
115 return internal.Pointer{}, xerrors.New("per-CPU value requires slice")
115 return internal.Pointer{}, errors.New("per-CPU value requires slice")
116116 }
117117
118118 possibleCPUs, err := internal.PossibleCPUs()
123123 sliceValue := reflect.ValueOf(slice)
124124 sliceLen := sliceValue.Len()
125125 if sliceLen > possibleCPUs {
126 return internal.Pointer{}, xerrors.Errorf("per-CPU value exceeds number of CPUs")
126 return internal.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs")
127127 }
128128
129129 alignedElemLength := align(elemLength, 8)
150150 func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error {
151151 slicePtrType := reflect.TypeOf(slicePtr)
152152 if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {
153 return xerrors.Errorf("per-cpu value requires pointer to slice")
153 return fmt.Errorf("per-cpu value requires pointer to slice")
154154 }
155155
156156 possibleCPUs, err := internal.PossibleCPUs()
169169
170170 step := len(buf) / possibleCPUs
171171 if step < elemLength {
172 return xerrors.Errorf("per-cpu element length is larger than available data")
172 return fmt.Errorf("per-cpu element length is larger than available data")
173173 }
174174 for i := 0; i < possibleCPUs; i++ {
175175 var elem interface{}
187187
188188 err := unmarshalBytes(elem, elemBytes)
189189 if err != nil {
190 return xerrors.Errorf("cpu %d: %w", i, err)
190 return fmt.Errorf("cpu %d: %w", i, err)
191191 }
192192
193193 buf = buf[step:]
11
22 import (
33 "encoding/binary"
4 "errors"
45 "fmt"
56 "io"
67 "math"
1011 "github.com/cilium/ebpf"
1112 "github.com/cilium/ebpf/internal"
1213 "github.com/cilium/ebpf/internal/unix"
13
14 "golang.org/x/xerrors"
1514 )
1615
1716 var (
18 errClosed = xerrors.New("perf reader was closed")
19 errEOR = xerrors.New("end of ring")
17 errClosed = errors.New("perf reader was closed")
18 errEOR = errors.New("end of ring")
2019 )
2120
2221 // perfEventHeader must match 'struct perf_event_header` in <linux/perf_event.h>.
2827
2928 func addToEpoll(epollfd, fd int, cpu int) error {
3029 if int64(cpu) > math.MaxInt32 {
31 return xerrors.Errorf("unsupported CPU number: %d", cpu)
30 return fmt.Errorf("unsupported CPU number: %d", cpu)
3231 }
3332
3433 // The representation of EpollEvent isn't entirely accurate.
4241 }
4342
4443 if err := unix.EpollCtl(epollfd, unix.EPOLL_CTL_ADD, fd, &event); err != nil {
45 return xerrors.Errorf("can't add fd to epoll: %v", err)
44 return fmt.Errorf("can't add fd to epoll: %v", err)
4645 }
4746 return nil
4847 }
5857 CPU int
5958
6059 // The data submitted via bpf_perf_event_output.
61 // They are padded with 0 to have a 64-bit alignment.
62 // If you are using variable length samples you need to take
63 // this into account.
60 // Due to a kernel bug, this can contain between 0 and 7 bytes of trailing
61 // garbage from the ring depending on the input sample's length.
6462 RawSample []byte
6563
6664 // The number of samples which could not be output, since
8785 }
8886
8987 if err != nil {
90 return Record{}, xerrors.Errorf("can't read event header: %v", err)
88 return Record{}, fmt.Errorf("can't read event header: %v", err)
9189 }
9290
9391 switch header.Type {
113111
114112 err := binary.Read(rd, internal.NativeEndian, &lostHeader)
115113 if err != nil {
116 return 0, xerrors.Errorf("can't read lost records header: %v", err)
114 return 0, fmt.Errorf("can't read lost records header: %v", err)
117115 }
118116
119117 return lostHeader.Lost, nil
123121 // This must match 'struct perf_event_sample in kernel sources.
124122 var size uint32
125123 if err := binary.Read(rd, internal.NativeEndian, &size); err != nil {
126 return nil, xerrors.Errorf("can't read sample size: %v", err)
124 return nil, fmt.Errorf("can't read sample size: %v", err)
127125 }
128126
129127 data := make([]byte, int(size))
130128 if _, err := io.ReadFull(rd, data); err != nil {
131 return nil, xerrors.Errorf("can't read sample: %v", err)
129 return nil, fmt.Errorf("can't read sample: %v", err)
132130 }
133131 return data, nil
134132 }
182180 // NewReaderWithOptions creates a new reader with the given options.
183181 func NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions) (pr *Reader, err error) {
184182 if perCPUBuffer < 1 {
185 return nil, xerrors.New("perCPUBuffer must be larger than 0")
183 return nil, errors.New("perCPUBuffer must be larger than 0")
186184 }
187185
188186 epollFd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC)
189187 if err != nil {
190 return nil, xerrors.Errorf("can't create epoll fd: %v", err)
188 return nil, fmt.Errorf("can't create epoll fd: %v", err)
191189 }
192190
193191 var (
215213 // Hence we have to create a ring for each CPU.
216214 for i := 0; i < nCPU; i++ {
217215 ring, err := newPerfEventRing(i, perCPUBuffer, opts.Watermark)
218 if xerrors.Is(err, unix.ENODEV) {
216 if errors.Is(err, unix.ENODEV) {
219217 // The requested CPU is currently offline, skip it.
220218 rings = append(rings, nil)
221219 pauseFds = append(pauseFds, -1)
223221 }
224222
225223 if err != nil {
226 return nil, xerrors.Errorf("failed to create perf ring for CPU %d: %v", i, err)
224 return nil, fmt.Errorf("failed to create perf ring for CPU %d: %v", i, err)
227225 }
228226 rings = append(rings, ring)
229227 pauseFds = append(pauseFds, ring.fd)
281279 internal.NativeEndian.PutUint64(value[:], 1)
282280 _, err = unix.Write(pr.closeFd, value[:])
283281 if err != nil {
284 err = xerrors.Errorf("can't write event fd: %v", err)
282 err = fmt.Errorf("can't write event fd: %v", err)
285283 return
286284 }
287285
308306 pr.array.Close()
309307 })
310308 if err != nil {
311 return xerrors.Errorf("close PerfReader: %w", err)
309 return fmt.Errorf("close PerfReader: %w", err)
312310 }
313311 return nil
314312 }
316314 // Read the next record from the perf ring buffer.
317315 //
318316 // The function blocks until there are at least Watermark bytes in one
319 // of the per CPU buffers.
320 //
321 // Records from buffers below the Watermark are not returned.
317 // of the per CPU buffers. Records from buffers below the Watermark
318 // are not returned.
319 //
320 // Records can contain between 0 and 7 bytes of trailing garbage from the ring
321 // depending on the input sample's length.
322322 //
323323 // Calling Close interrupts the function.
324324 func (pr *Reader) Read() (Record, error) {
386386 }
387387
388388 for i := range pr.pauseFds {
389 if err := pr.array.Delete(uint32(i)); err != nil && !xerrors.Is(err, ebpf.ErrKeyNotExist) {
390 return xerrors.Errorf("could't delete event fd for CPU %d: %w", i, err)
389 if err := pr.array.Delete(uint32(i)); err != nil && !errors.Is(err, ebpf.ErrKeyNotExist) {
390 return fmt.Errorf("could't delete event fd for CPU %d: %w", i, err)
391391 }
392392 }
393393
411411 }
412412
413413 if err := pr.array.Put(uint32(i), uint32(fd)); err != nil {
414 return xerrors.Errorf("couldn't put event fd %d for CPU %d: %w", fd, i, err)
414 return fmt.Errorf("couldn't put event fd %d for CPU %d: %w", fd, i, err)
415415 }
416416 }
417417
425425 // IsClosed returns true if the error occurred because
426426 // a Reader was closed.
427427 func IsClosed(err error) bool {
428 return xerrors.Is(err, errClosed)
428 return errors.Is(err, errClosed)
429429 }
430430
431431 type unknownEventError struct {
440440 // because an unknown event was submitted to the perf event ring.
441441 func IsUnknownEvent(err error) bool {
442442 var uee *unknownEventError
443 return xerrors.As(err, &uee)
444 }
443 return errors.As(err, &uee)
444 }
2020 )
2121
2222 func TestMain(m *testing.M) {
23 err := unix.Setrlimit(8, &unix.Rlimit{
23 err := unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{
2424 Cur: unix.RLIM_INFINITY,
2525 Max: unix.RLIM_INFINITY,
2626 })
00 package perf
11
22 import (
3 "errors"
4 "fmt"
35 "io"
6 "math"
47 "os"
58 "runtime"
69 "sync/atomic"
710 "unsafe"
811
912 "github.com/cilium/ebpf/internal/unix"
10
11 "golang.org/x/xerrors"
1213 )
1314
1415 // perfEventRing is a page of metadata followed by
2223
2324 func newPerfEventRing(cpu, perCPUBuffer, watermark int) (*perfEventRing, error) {
2425 if watermark >= perCPUBuffer {
25 return nil, xerrors.New("watermark must be smaller than perCPUBuffer")
26 return nil, errors.New("watermark must be smaller than perCPUBuffer")
2627 }
27
28 // Round to nearest page boundary and allocate
29 // an extra page for meta data
30 pageSize := os.Getpagesize()
31 nPages := (perCPUBuffer + pageSize - 1) / pageSize
32 size := (1 + nPages) * pageSize
3328
3429 fd, err := createPerfEvent(cpu, watermark)
3530 if err != nil {
4136 return nil, err
4237 }
4338
44 mmap, err := unix.Mmap(fd, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
39 mmap, err := unix.Mmap(fd, 0, perfBufferSize(perCPUBuffer), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
4540 if err != nil {
4641 unix.Close(fd)
47 return nil, err
42 return nil, fmt.Errorf("can't mmap: %v", err)
4843 }
4944
5045 // This relies on the fact that we allocate an extra metadata page,
6257 runtime.SetFinalizer(ring, (*perfEventRing).Close)
6358
6459 return ring, nil
60 }
61
62 // mmapBufferSize returns a valid mmap buffer size for use with perf_event_open (1+2^n pages)
63 func perfBufferSize(perCPUBuffer int) int {
64 pageSize := os.Getpagesize()
65
66 // Smallest whole number of pages
67 nPages := (perCPUBuffer + pageSize - 1) / pageSize
68
69 // Round up to nearest power of two number of pages
70 nPages = int(math.Pow(2, math.Ceil(math.Log2(float64(nPages)))))
71
72 // Add one for metadata
73 nPages += 1
74
75 return nPages * pageSize
6576 }
6677
6778 func (ring *perfEventRing) Close() {
89100 attr.Size = uint32(unsafe.Sizeof(attr))
90101 fd, err := unix.PerfEventOpen(&attr, -1, cpu, -1, unix.PERF_FLAG_FD_CLOEXEC)
91102 if err != nil {
92 return -1, xerrors.Errorf("can't create perf event: %w", err)
103 return -1, fmt.Errorf("can't create perf event: %w", err)
93104 }
94105 return fd, nil
95106 }
22 import (
33 "bytes"
44 "io"
5 "os"
56 "testing"
67
78 "github.com/cilium/ebpf/internal/unix"
6162
6263 return newRingReader(&meta, ring)
6364 }
65
66 func TestPerfEventRing(t *testing.T) {
67 check := func(buffer, watermark int) {
68 ring, err := newPerfEventRing(0, buffer, watermark)
69 if err != nil {
70 t.Fatal(err)
71 }
72
73 size := len(ring.ringReader.ring)
74
75 // Ring size should be at least as big as buffer
76 if size < buffer {
77 t.Fatalf("ring size %d smaller than buffer %d", size, buffer)
78 }
79
80 // Ring size should be of the form 2^n pages (meta page has already been removed)
81 if size%os.Getpagesize() != 0 {
82 t.Fatalf("ring size %d not whole number of pages (pageSize %d)", size, os.Getpagesize())
83 }
84 nPages := size / os.Getpagesize()
85 if nPages&(nPages-1) != 0 {
86 t.Fatalf("ring size %d (%d pages) not a power of two pages (pageSize %d)", size, nPages, os.Getpagesize())
87 }
88 }
89
90 // watermark > buffer
91 _, err := newPerfEventRing(0, 8192, 8193)
92 if err == nil {
93 t.Fatal("watermark > buffer allowed")
94 }
95
96 // watermark == buffer
97 _, err = newPerfEventRing(0, 8192, 8192)
98 if err == nil {
99 t.Fatal("watermark == buffer allowed")
100 }
101
102 // buffer not a power of two, watermark < buffer
103 check(8193, 8192)
104
105 // large buffer not a multiple of page size at all (prime)
106 check(65537, 8192)
107 }
11
22 import (
33 "bytes"
4 "encoding/binary"
5 "errors"
46 "fmt"
57 "math"
68 "strings"
79 "time"
8 "unsafe"
910
1011 "github.com/cilium/ebpf/asm"
1112 "github.com/cilium/ebpf/internal"
1213 "github.com/cilium/ebpf/internal/btf"
1314 "github.com/cilium/ebpf/internal/unix"
14
15 "golang.org/x/xerrors"
1615 )
1716
1817 // ErrNotSupported is returned whenever the kernel doesn't support a feature.
4645 type ProgramSpec struct {
4746 // Name is passed to the kernel as a debug aid. Must only contain
4847 // alpha numeric and '_' characters.
49 Name string
50 Type ProgramType
51 AttachType AttachType
52 Instructions asm.Instructions
53 License string
48 Name string
49 // Type determines at which hook in the kernel a program will run.
50 Type ProgramType
51 AttachType AttachType
52 // Name of a kernel data structure to attach to. It's interpretation
53 // depends on Type and AttachType.
54 AttachTo string
55 Instructions asm.Instructions
56
57 // License of the program. Some helpers are only available if
58 // the license is deemed compatible with the GPL.
59 //
60 // See https://www.kernel.org/doc/html/latest/process/license-rules.html#id1
61 License string
62
63 // Version used by tracing programs.
64 //
65 // Deprecated: superseded by BTF.
5466 KernelVersion uint32
5567
5668 // The BTF associated with this program. Changing Instructions
5769 // will most likely invalidate the contained data, and may
5870 // result in errors when attempting to load it into the kernel.
5971 BTF *btf.Program
72
73 // The byte order this program was compiled for, may be nil.
74 ByteOrder binary.ByteOrder
6075 }
6176
6277 // Copy returns a copy of the spec.
7994 // otherwise it is empty.
8095 VerifierLog string
8196
82 fd *internal.FD
83 name string
84 abi ProgramABI
97 fd *internal.FD
98 name string
99 abi ProgramABI
100 attachType AttachType
85101 }
86102
87103 // NewProgram creates a new Program.
102118 }
103119
104120 handle, err := btf.NewHandle(btf.ProgramSpec(spec.BTF))
105 if err != nil && !xerrors.Is(err, btf.ErrNotSupported) {
106 return nil, xerrors.Errorf("can't load BTF: %w", err)
121 if err != nil && !errors.Is(err, btf.ErrNotSupported) {
122 return nil, fmt.Errorf("can't load BTF: %w", err)
107123 }
108124
109125 return newProgramWithBTF(spec, handle, opts)
147163 }
148164
149165 err = internal.ErrorWithLog(err, logBuf, logErr)
150 return nil, xerrors.Errorf("can't load program: %w", err)
166 return nil, fmt.Errorf("can't load program: %w", err)
151167 }
152168
153169 // NewProgramFromFD creates a program from a raw fd.
157173 // Requires at least Linux 4.11.
158174 func NewProgramFromFD(fd int) (*Program, error) {
159175 if fd < 0 {
160 return nil, xerrors.New("invalid fd")
176 return nil, errors.New("invalid fd")
161177 }
162178 bpfFd := internal.NewFD(uint32(fd))
163179
180196
181197 func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr, error) {
182198 if len(spec.Instructions) == 0 {
183 return nil, xerrors.New("Instructions cannot be empty")
199 return nil, errors.New("Instructions cannot be empty")
184200 }
185201
186202 if len(spec.License) == 0 {
187 return nil, xerrors.New("License cannot be empty")
203 return nil, errors.New("License cannot be empty")
204 }
205
206 if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian {
207 return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian)
188208 }
189209
190210 buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize))
213233
214234 recSize, bytes, err := btf.ProgramLineInfos(spec.BTF)
215235 if err != nil {
216 return nil, xerrors.Errorf("can't get BTF line infos: %w", err)
236 return nil, fmt.Errorf("can't get BTF line infos: %w", err)
217237 }
218238 attr.lineInfoRecSize = recSize
219239 attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
221241
222242 recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF)
223243 if err != nil {
224 return nil, xerrors.Errorf("can't get BTF function infos: %w", err)
244 return nil, fmt.Errorf("can't get BTF function infos: %w", err)
225245 }
226246 attr.funcInfoRecSize = recSize
227247 attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
228248 attr.funcInfo = internal.NewSlicePointer(bytes)
249 }
250
251 if spec.AttachTo != "" {
252 target, err := resolveBTFType(spec.AttachTo, spec.Type, spec.AttachType)
253 if err != nil {
254 return nil, err
255 }
256 if target != nil {
257 attr.attachBTFID = target.ID()
258 }
229259 }
230260
231261 return attr, nil
269299
270300 dup, err := p.fd.Dup()
271301 if err != nil {
272 return nil, xerrors.Errorf("can't clone program: %w", err)
302 return nil, fmt.Errorf("can't clone program: %w", err)
273303 }
274304
275305 return newProgram(dup, p.name, &p.abi), nil
279309 //
280310 // This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional
281311 func (p *Program) Pin(fileName string) error {
282 if err := bpfPinObject(fileName, p.fd); err != nil {
283 return xerrors.Errorf("can't pin program: %w", err)
312 if err := internal.BPFObjPin(fileName, p.fd); err != nil {
313 return fmt.Errorf("can't pin program: %w", err)
284314 }
285315 return nil
286316 }
304334 func (p *Program) Test(in []byte) (uint32, []byte, error) {
305335 ret, out, _, err := p.testRun(in, 1, nil)
306336 if err != nil {
307 return ret, nil, xerrors.Errorf("can't test program: %w", err)
337 return ret, nil, fmt.Errorf("can't test program: %w", err)
308338 }
309339 return ret, out, nil
310340 }
323353 func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) {
324354 ret, _, total, err := p.testRun(in, repeat, reset)
325355 if err != nil {
326 return ret, total, xerrors.Errorf("can't benchmark program: %w", err)
356 return ret, total, fmt.Errorf("can't benchmark program: %w", err)
327357 }
328358 return ret, total, nil
329359 }
330360
331 var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() bool {
361 var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() (bool, error) {
332362 prog, err := NewProgram(&ProgramSpec{
333363 Type: SocketFilter,
334364 Instructions: asm.Instructions{
339369 })
340370 if err != nil {
341371 // This may be because we lack sufficient permissions, etc.
342 return false
372 return false, err
343373 }
344374 defer prog.Close()
345
346 fd, err := prog.fd.Value()
347 if err != nil {
348 return false
349 }
350375
351376 // Programs require at least 14 bytes input
352377 in := make([]byte, 14)
353378 attr := bpfProgTestRunAttr{
354 fd: fd,
379 fd: uint32(prog.FD()),
355380 dataSizeIn: uint32(len(in)),
356381 dataIn: internal.NewSlicePointer(in),
357382 }
358383
359 _, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
384 err = bpfProgTestRun(&attr)
360385
361386 // Check for EINVAL specifically, rather than err != nil since we
362387 // otherwise misdetect due to insufficient permissions.
363 return !xerrors.Is(err, unix.EINVAL)
388 return !errors.Is(err, unix.EINVAL), nil
364389 })
365390
366391 func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, time.Duration, error) {
402427 }
403428
404429 for {
405 _, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
430 err = bpfProgTestRun(&attr)
406431 if err == nil {
407432 break
408433 }
409434
410 if xerrors.Is(err, unix.EINTR) {
435 if errors.Is(err, unix.EINTR) {
411436 if reset != nil {
412437 reset()
413438 }
414439 continue
415440 }
416441
417 return 0, nil, 0, xerrors.Errorf("can't run test: %w", err)
442 return 0, nil, 0, fmt.Errorf("can't run test: %w", err)
418443 }
419444
420445 if int(attr.dataSizeOut) > cap(out) {
430455
431456 func unmarshalProgram(buf []byte) (*Program, error) {
432457 if len(buf) != 4 {
433 return nil, xerrors.New("program id requires 4 byte value")
458 return nil, errors.New("program id requires 4 byte value")
434459 }
435460
436461 // Looking up an entry in a nested map or prog array returns an id,
451476 return buf, nil
452477 }
453478
454 // Attach a Program to a container object fd
479 // Attach a Program.
480 //
481 // Deprecated: use link.RawAttachProgram instead.
455482 func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
456483 if fd < 0 {
457 return xerrors.New("invalid fd")
484 return errors.New("invalid fd")
458485 }
459486
460487 pfd, err := p.fd.Value()
462489 return err
463490 }
464491
465 attr := bpfProgAlterAttr{
466 targetFd: uint32(fd),
467 attachBpfFd: pfd,
468 attachType: uint32(typ),
469 attachFlags: uint32(flags),
470 }
471
472 return bpfProgAlter(_ProgAttach, &attr)
473 }
474
475 // Detach a Program from a container object fd
492 attr := internal.BPFProgAttachAttr{
493 TargetFd: uint32(fd),
494 AttachBpfFd: pfd,
495 AttachType: uint32(typ),
496 AttachFlags: uint32(flags),
497 }
498
499 return internal.BPFProgAttach(&attr)
500 }
501
502 // Detach a Program.
503 //
504 // Deprecated: use link.RawDetachProgram instead.
476505 func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error {
477506 if fd < 0 {
478 return xerrors.New("invalid fd")
507 return errors.New("invalid fd")
508 }
509
510 if flags != 0 {
511 return errors.New("flags must be zero")
479512 }
480513
481514 pfd, err := p.fd.Value()
483516 return err
484517 }
485518
486 attr := bpfProgAlterAttr{
487 targetFd: uint32(fd),
488 attachBpfFd: pfd,
489 attachType: uint32(typ),
490 attachFlags: uint32(flags),
491 }
492
493 return bpfProgAlter(_ProgDetach, &attr)
519 attr := internal.BPFProgDetachAttr{
520 TargetFd: uint32(fd),
521 AttachBpfFd: pfd,
522 AttachType: uint32(typ),
523 }
524
525 return internal.BPFProgDetach(&attr)
494526 }
495527
496528 // LoadPinnedProgram loads a Program from a BPF file.
497529 //
498530 // Requires at least Linux 4.11.
499531 func LoadPinnedProgram(fileName string) (*Program, error) {
500 fd, err := bpfGetObject(fileName)
532 fd, err := internal.BPFObjGet(fileName)
501533 if err != nil {
502534 return nil, err
503535 }
505537 name, abi, err := newProgramABIFromFd(fd)
506538 if err != nil {
507539 _ = fd.Close()
508 return nil, xerrors.Errorf("can't get ABI for %s: %w", fileName, err)
540 return nil, fmt.Errorf("can't get ABI for %s: %w", fileName, err)
509541 }
510542
511543 return newProgram(fd, name, abi), nil
531563 //
532564 // Returns ErrNotExist, if there is no next eBPF program.
533565 func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
534 id, err := objGetNextID(_ProgGetNextID, uint32(startID))
566 id, err := objGetNextID(internal.BPF_PROG_GET_NEXT_ID, uint32(startID))
535567 return ProgramID(id), err
536568 }
537569
539571 //
540572 // Returns ErrNotExist, if there is no eBPF program with the given id.
541573 func NewProgramFromID(id ProgramID) (*Program, error) {
542 fd, err := bpfObjGetFDByID(_ProgGetFDByID, uint32(id))
574 fd, err := bpfObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id))
543575 if err != nil {
544576 return nil, err
545577 }
561593 }
562594 return ProgramID(info.id), nil
563595 }
596
597 func findKernelType(name string, typ btf.Type) error {
598 kernel, err := btf.LoadKernelSpec()
599 if err != nil {
600 return fmt.Errorf("can't load kernel spec: %w", err)
601 }
602
603 return kernel.FindType(name, typ)
604 }
605
606 func resolveBTFType(name string, progType ProgramType, attachType AttachType) (btf.Type, error) {
607 type match struct {
608 p ProgramType
609 a AttachType
610 }
611
612 target := match{progType, attachType}
613 switch target {
614 case match{Tracing, AttachTraceIter}:
615 var target btf.Func
616 if err := findKernelType("bpf_iter_"+name, &target); err != nil {
617 return nil, fmt.Errorf("can't resolve BTF for iterator %s: %w", name, err)
618 }
619
620 return &target, nil
621
622 default:
623 return nil, nil
624 }
625 }
11
22 import (
33 "bytes"
4 "encoding/binary"
5 "errors"
46 "fmt"
57 "io/ioutil"
68 "math"
1618 "github.com/cilium/ebpf/internal"
1719 "github.com/cilium/ebpf/internal/testutils"
1820 "github.com/cilium/ebpf/internal/unix"
19 "golang.org/x/xerrors"
2021 )
2122
2223 func TestProgramRun(t *testing.T) {
269270 }
270271
271272 var ve *internal.VerifierError
272 if !xerrors.As(err, &ve) {
273 if !errors.As(err, &ve) {
273274 t.Error("Error is not a VerifierError")
274275 }
275276 }
386387 prog2.Close()
387388 }
388389
389 func TestProgramAlter(t *testing.T) {
390 testutils.SkipOnOldKernel(t, "4.13", "SkSKB type")
391
392 var err error
393 var prog *Program
394 prog, err = NewProgram(&ProgramSpec{
395 Type: SkSKB,
396 Instructions: asm.Instructions{
397 asm.LoadImm(asm.R0, 0, asm.DWord),
398 asm.Return(),
399 },
400 License: "MIT",
401 })
402 if err != nil {
403 t.Fatal(err)
404 }
405 defer prog.Close()
406
407 var sockMap *Map
408 sockMap, err = NewMap(&MapSpec{
409 Type: MapType(15), // BPF_MAP_TYPE_SOCKMAP
410 KeySize: 4,
411 ValueSize: 4,
412 MaxEntries: 2,
413 })
414 if err != nil {
415 t.Fatal(err)
416 }
417 defer sockMap.Close()
418
419 if err := prog.Attach(sockMap.FD(), AttachSkSKBStreamParser, AttachFlags(0)); err != nil {
420 t.Fatal(err)
421 }
422 if err := prog.Detach(sockMap.FD(), AttachSkSKBStreamParser, AttachFlags(0)); err != nil {
423 t.Fatal(err)
424 }
425 }
426
427390 func TestHaveProgTestRun(t *testing.T) {
428391 testutils.CheckFeatureTest(t, haveProgTestRun)
429392 }
457420 for {
458421 last := next
459422 if next, err = ProgramGetNextID(last); err != nil {
460 if !xerrors.Is(err, ErrNotExist) {
423 if !errors.Is(err, ErrNotExist) {
461424 t.Fatal("Expected ErrNotExist, got:", err)
462425 }
463426 break
496459
497460 // As there can be multiple programs, we use max(uint32) as ProgramID to trigger an expected error.
498461 _, err = NewProgramFromID(ProgramID(math.MaxUint32))
499 if !xerrors.Is(err, ErrNotExist) {
462 if !errors.Is(err, ErrNotExist) {
500463 t.Fatal("Expected ErrNotExist, got:", err)
464 }
465 }
466
467 func TestProgramRejectIncorrectByteOrder(t *testing.T) {
468 spec := socketFilterSpec.Copy()
469
470 spec.ByteOrder = binary.BigEndian
471 if internal.NativeEndian == binary.BigEndian {
472 spec.ByteOrder = binary.LittleEndian
473 }
474
475 _, err := NewProgram(spec)
476 if err == nil {
477 t.Error("Incorrect ByteOrder should be rejected at load time")
501478 }
502479 }
503480
00 eBPF
11 -------
2 [![](https://godoc.org/github.com/cilium/ebpf?status.svg)](https://godoc.org/github.com/cilium/ebpf)
2 [![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf)
33
44 eBPF is a pure Go library that provides utilities for loading, compiling, and debugging eBPF programs. It has minimal external dependencies and is intended to be used in long running processes.
55
6 [ebpf/asm](https://godoc.org/github.com/cilium/ebpf/asm) contains a basic assembler.
6 * [ebpf/link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF to various hooks.
7 * [ebpf/asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic assembler.
78
89 The library is maintained by [Cloudflare](https://www.cloudflare.com) and [Cilium](https://www.cilium.io). Feel free to [join](https://cilium.herokuapp.com/) the [libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack.
910
1414 export GOPROXY=file:///run/go-root/pkg/mod/cache/download
1515 export GOCACHE=/run/go-cache
1616
17 elfs=""
18 if [[ -d "/run/input/bpf" ]]; then
19 elfs="/run/input/bpf"
20 fi
21
1722 echo Running tests...
18 /usr/local/bin/go test -coverprofile="$1/coverage.txt" -covermode=atomic -v ./...
23 # TestLibBPFCompat runs separately to pass the "-elfs" flag only for it: https://github.com/cilium/ebpf/pull/119
24 /usr/local/bin/go test -v -elfs "$elfs" -run TestLibBPFCompat
25 /usr/local/bin/go test -v ./...
1926 touch "$1/success"
2027 exit 0
2128 fi
3845 fi
3946
4047 readonly kernel="linux-${kernel_version}.bz"
48 readonly selftests="linux-${kernel_version}-selftests-bpf.bz"
49 readonly input="$(mktemp -d)"
4150 readonly output="$(mktemp -d)"
42 readonly tmp_dir="${TMPDIR:-$(mktemp -d)}"
51 readonly tmp_dir="${TMPDIR:-/tmp}"
52 readonly branch="${BRANCH:-master}"
4353
44 test -e "${tmp_dir}/${kernel}" || {
45 echo Fetching "${kernel}"
46 curl --fail -L "https://github.com/newtools/ci-kernels/blob/master/${kernel}?raw=true" -o "${tmp_dir}/${kernel}"
54 fetch() {
55 echo Fetching "${1}"
56 wget -nv -N -P "${tmp_dir}" "https://github.com/cilium/ci-kernels/raw/${branch}/${1}"
4757 }
58
59 fetch "${kernel}"
60
61 if fetch "${selftests}"; then
62 mkdir "${input}/bpf"
63 tar --strip-components=4 -xjf "${tmp_dir}/${selftests}" -C "${input}/bpf"
64 else
65 echo "No selftests found, disabling"
66 fi
4867
4968 echo Testing on "${kernel_version}"
5069 $sudo virtme-run --kimg "${tmp_dir}/${kernel}" --memory 512M --pwd \
70 --rwdir=/run/input="${input}" \
5171 --rwdir=/run/output="${output}" \
5272 --rodir=/run/go-path="$(go env GOPATH)" \
5373 --rwdir=/run/go-cache="$(go env GOCACHE)" \
54 --script-sh "$(realpath "$0") --in-vm /run/output"
74 --script-sh "$(realpath "$0") --in-vm /run/output" \
75 --qemu-opts -smp 2 # need at least two CPUs for some tests
5576
5677 if [[ ! -e "${output}/success" ]]; then
5778 echo "Test failed on ${kernel_version}"
5879 exit 1
5980 else
6081 echo "Test successful on ${kernel_version}"
61 if [[ -v CODECOV_TOKEN ]]; then
62 curl --fail -s https://codecov.io/bash > "${tmp_dir}/codecov.sh"
63 chmod +x "${tmp_dir}/codecov.sh"
64 "${tmp_dir}/codecov.sh" -f "${output}/coverage.txt"
65 fi
82 # if [[ -v CODECOV_TOKEN ]]; then
83 # curl --fail -s https://codecov.io/bash > "${tmp_dir}/codecov.sh"
84 # chmod +x "${tmp_dir}/codecov.sh"
85 # "${tmp_dir}/codecov.sh" -f "${output}/coverage.txt"
86 # fi
6687 fi
6788
89 $sudo rm -r "${input}"
6890 $sudo rm -r "${output}"
00 package ebpf
11
22 import (
3 "path/filepath"
3 "errors"
4 "fmt"
5 "os"
46 "unsafe"
57
68 "github.com/cilium/ebpf/internal"
79 "github.com/cilium/ebpf/internal/btf"
810 "github.com/cilium/ebpf/internal/unix"
9
10 "golang.org/x/xerrors"
1111 )
1212
1313 // Generic errors returned by BPF syscalls.
1414 var (
15 ErrNotExist = xerrors.New("requested object does not exit")
15 ErrNotExist = errors.New("requested object does not exist")
1616 )
1717
1818 // bpfObjName is a null-terminated string made up of
7676 maxEntries uint32
7777 flags uint32
7878 mapName bpfObjName // since 4.15 ad5b177bd73f
79 }
80
81 type bpfPinObjAttr struct {
82 fileName internal.Pointer
83 fd uint32
84 padding uint32
8579 }
8680
8781 type bpfProgLoadAttr struct {
10498 lineInfoRecSize uint32
10599 lineInfo internal.Pointer
106100 lineInfoCnt uint32
101 attachBTFID btf.TypeID
102 attachProgFd uint32
107103 }
108104
109105 type bpfProgInfo struct {
132128 duration uint32
133129 }
134130
135 type bpfProgAlterAttr struct {
136 targetFd uint32
137 attachBpfFd uint32
138 attachType uint32
139 attachFlags uint32
140 }
141
142131 type bpfObjGetInfoByFDAttr struct {
143132 fd uint32
144133 infoLen uint32
162151
163152 func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) {
164153 for {
165 fd, err := internal.BPF(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
154 fd, err := internal.BPF(internal.BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
166155 // As of ~4.20 the verifier can be interrupted by a signal,
167156 // and returns EAGAIN in that case.
168157 if err == unix.EAGAIN {
177166 }
178167 }
179168
180 func bpfProgAlter(cmd int, attr *bpfProgAlterAttr) error {
181 _, err := internal.BPF(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
169 func bpfProgTestRun(attr *bpfProgTestRunAttr) error {
170 _, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
182171 return err
183172 }
184173
185174 func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) {
186 fd, err := internal.BPF(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
175 fd, err := internal.BPF(internal.BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
176 if errors.Is(err, os.ErrPermission) {
177 return nil, errors.New("permission denied or insufficient rlimit to lock memory for map")
178 }
179
187180 if err != nil {
188181 return nil, err
189182 }
191184 return internal.NewFD(uint32(fd)), nil
192185 }
193186
194 var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool {
187 var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() (bool, error) {
195188 inner, err := bpfMapCreate(&bpfMapCreateAttr{
196189 mapType: Array,
197190 keySize: 4,
199192 maxEntries: 1,
200193 })
201194 if err != nil {
202 return false
195 return false, err
203196 }
204197 defer inner.Close()
205198
212205 innerMapFd: innerFd,
213206 })
214207 if err != nil {
215 return false
208 return false, nil
216209 }
217210
218211 _ = nested.Close()
219 return true
212 return true, nil
220213 })
221214
222 var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() bool {
215 var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() (bool, error) {
223216 // This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since
224217 // BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check.
225218 m, err := bpfMapCreate(&bpfMapCreateAttr{
230223 flags: unix.BPF_F_RDONLY_PROG,
231224 })
232225 if err != nil {
233 return false
226 return false, nil
234227 }
235228 _ = m.Close()
236 return true
229 return true, nil
237230 })
238231
239232 func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error {
247240 key: key,
248241 value: valueOut,
249242 }
250 _, err = internal.BPF(_MapLookupElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
243 _, err = internal.BPF(internal.BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
251244 return wrapMapError(err)
252245 }
253246
262255 key: key,
263256 value: valueOut,
264257 }
265 _, err = internal.BPF(_MapLookupAndDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
258 _, err = internal.BPF(internal.BPF_MAP_LOOKUP_AND_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
266259 return wrapMapError(err)
267260 }
268261
278271 value: valueOut,
279272 flags: flags,
280273 }
281 _, err = internal.BPF(_MapUpdateElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
274 _, err = internal.BPF(internal.BPF_MAP_UPDATE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
282275 return wrapMapError(err)
283276 }
284277
292285 mapFd: fd,
293286 key: key,
294287 }
295 _, err = internal.BPF(_MapDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
288 _, err = internal.BPF(internal.BPF_MAP_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
296289 return wrapMapError(err)
297290 }
298291
307300 key: key,
308301 value: nextKeyOut,
309302 }
310 _, err = internal.BPF(_MapGetNextKey, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
303 _, err = internal.BPF(internal.BPF_MAP_GET_NEXT_KEY, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
311304 return wrapMapError(err)
312305 }
313306
314 func objGetNextID(cmd int, start uint32) (uint32, error) {
307 func objGetNextID(cmd internal.BPFCmd, start uint32) (uint32, error) {
315308 attr := bpfObjGetNextIDAttr{
316309 startID: start,
317310 }
323316 if err == nil {
324317 return nil
325318 }
326 if xerrors.Is(err, unix.ENOENT) {
327 return xerrors.Errorf("%w", ErrNotExist)
328 }
329
330 return xerrors.New(err.Error())
319 if errors.Is(err, unix.ENOENT) {
320 return fmt.Errorf("%w", ErrNotExist)
321 }
322
323 return errors.New(err.Error())
331324 }
332325
333326 func wrapMapError(err error) error {
335328 return nil
336329 }
337330
338 if xerrors.Is(err, unix.ENOENT) {
331 if errors.Is(err, unix.ENOENT) {
339332 return ErrKeyNotExist
340333 }
341334
342 return xerrors.New(err.Error())
335 if errors.Is(err, unix.EEXIST) {
336 return ErrKeyExist
337 }
338
339 return errors.New(err.Error())
343340 }
344341
345342 func bpfMapFreeze(m *internal.FD) error {
351348 attr := bpfMapFreezeAttr{
352349 mapFd: fd,
353350 }
354 _, err = internal.BPF(_MapFreeze, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
351 _, err = internal.BPF(internal.BPF_MAP_FREEZE, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
355352 return err
356 }
357
358 const bpfFSType = 0xcafe4a11
359
360 func bpfPinObject(fileName string, fd *internal.FD) error {
361 dirName := filepath.Dir(fileName)
362 var statfs unix.Statfs_t
363 if err := unix.Statfs(dirName, &statfs); err != nil {
364 return err
365 }
366 if uint64(statfs.Type) != bpfFSType {
367 return xerrors.Errorf("%s is not on a bpf filesystem", fileName)
368 }
369
370 value, err := fd.Value()
371 if err != nil {
372 return err
373 }
374
375 _, err = internal.BPF(_ObjPin, unsafe.Pointer(&bpfPinObjAttr{
376 fileName: internal.NewStringPointer(fileName),
377 fd: value,
378 }), 16)
379 if err != nil {
380 return xerrors.Errorf("pin object %s: %w", fileName, err)
381 }
382 return nil
383 }
384
385 func bpfGetObject(fileName string) (*internal.FD, error) {
386 ptr, err := internal.BPF(_ObjGet, unsafe.Pointer(&bpfPinObjAttr{
387 fileName: internal.NewStringPointer(fileName),
388 }), 16)
389 if err != nil {
390 return nil, xerrors.Errorf("get object %s: %w", fileName, err)
391 }
392 return internal.NewFD(uint32(ptr)), nil
393353 }
394354
395355 func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) error {
404364 infoLen: uint32(size),
405365 info: internal.NewPointer(info),
406366 }
407 _, err = internal.BPF(_ObjGetInfoByFD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
408 if err != nil {
409 return xerrors.Errorf("fd %d: %w", fd, err)
367 _, err = internal.BPF(internal.BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
368 if err != nil {
369 return fmt.Errorf("fd %d: %w", fd, err)
410370 }
411371 return nil
412372 }
414374 func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) {
415375 var info bpfProgInfo
416376 if err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
417 return nil, xerrors.Errorf("can't get program info: %w", err)
377 return nil, fmt.Errorf("can't get program info: %w", err)
418378 }
419379 return &info, nil
420380 }
423383 var info bpfMapInfo
424384 err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
425385 if err != nil {
426 return nil, xerrors.Errorf("can't get map info: %w", err)
386 return nil, fmt.Errorf("can't get map info: %w", err)
427387 }
428388 return &info, nil
429389 }
430390
431 var haveObjName = internal.FeatureTest("object names", "4.15", func() bool {
391 var haveObjName = internal.FeatureTest("object names", "4.15", func() (bool, error) {
432392 attr := bpfMapCreateAttr{
433393 mapType: Array,
434394 keySize: 4,
439399
440400 fd, err := bpfMapCreate(&attr)
441401 if err != nil {
442 return false
402 return false, nil
443403 }
444404
445405 _ = fd.Close()
446 return true
406 return true, nil
447407 })
448408
449 var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() bool {
409 var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() (bool, error) {
450410 if err := haveObjName(); err != nil {
451 return false
411 return false, err
452412 }
453413
454414 attr := bpfMapCreateAttr{
461421
462422 fd, err := bpfMapCreate(&attr)
463423 if err != nil {
464 return false
424 return false, nil
465425 }
466426
467427 _ = fd.Close()
468 return true
428 return true, nil
469429 })
470430
471 func bpfObjGetFDByID(cmd int, id uint32) (*internal.FD, error) {
431 func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) {
472432 attr := bpfGetFDByIDAttr{
473433 id: id,
474434 }
00 LLVM_PREFIX ?= /usr/bin
11 CLANG ?= $(LLVM_PREFIX)/clang
2 CFLAGS := -target bpf -O2 -g -Wall -Werror $(CFLAGS)
23
34 .PHONY: all clean
4 all: loader-clang-6.0.elf loader-clang-7.elf loader-clang-8.elf loader-clang-9.elf rewrite.elf invalid_map.elf
5 all: loader-clang-6.0-el.elf loader-clang-7-el.elf loader-clang-8-el.elf loader-clang-9-el.elf rewrite-el.elf invalid_map-el.elf raw_tracepoint-el.elf \
6 loader-clang-6.0-eb.elf loader-clang-7-eb.elf loader-clang-8-eb.elf loader-clang-9-eb.elf rewrite-eb.elf invalid_map-eb.elf raw_tracepoint-eb.elf
57
68 clean:
79 -$(RM) *.elf
810
9 loader-%.elf: loader.c
10 $* -target bpf -O2 -g \
11 -Wall -Werror \
12 -c $< -o $@
11 loader-%-el.elf: loader.c
12 $* $(CFLAGS) -mlittle-endian -c $< -o $@
1313
14 %.elf : %.c
15 $(CLANG) -target bpf -O2 -g \
16 -Wall -Werror \
17 -c $< -o $@
14 loader-%-eb.elf: loader.c
15 $* $(CFLAGS) -mbig-endian -c $< -o $@
1816
17 %-el.elf: %.c
18 $(CLANG) $(CFLAGS) -mlittle-endian -c $< -o $@
19
20 %-eb.elf : %.c
21 $(CLANG) $(CFLAGS) -mbig-endian -c $< -o $@
testdata/invalid_map.elf less more
Binary diff not shown
testdata/loader-clang-6.0.elf less more
Binary diff not shown
testdata/loader-clang-7.elf less more
Binary diff not shown
testdata/loader-clang-8.elf less more
Binary diff not shown
testdata/loader-clang-9.elf less more
Binary diff not shown
4141 __uint(max_entries, 1);
4242 __uint(map_flags, BPF_F_NO_PREALLOC);
4343 } btf_map __section(".maps");
44
45 struct {
46 __uint(type, BPF_MAP_TYPE_ARRAY);
47 __uint(key_size, 4);
48 __uint(value_size, 4);
49 __uint(max_entries, 1);
50 } btf_map2 __section(".maps");
4451 #endif
4552
4653 static int __attribute__((noinline)) static_fn(uint32_t arg) {
0 /* This file excercises the ELF loader. */
1
2 #include "common.h"
3
4 char __license[] __section("license") = "MIT";
5
6 struct bpf_args {
7 uint64_t args[0];
8 };
9
10 __section("raw_tracepoint/sched_process_exec") int sched_process_exec(struct bpf_args *ctx) {
11 return 0;
12 }
Binary diff not shown
Binary diff not shown
testdata/rewrite.elf less more
Binary diff not shown
00 package ebpf
11
2 //go:generate stringer -output types_string.go -type=MapType,ProgramType
2 //go:generate stringer -output types_string.go -type=MapType,ProgramType,AttachType
33
44 // MapType indicates the type map structure
55 // that will be initialized in the kernel.
8484
8585 // hasPerCPUValue returns true if the Map stores a value per CPU.
8686 func (mt MapType) hasPerCPUValue() bool {
87 if mt == PerCPUHash || mt == PerCPUArray {
87 if mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash {
8888 return true
8989 }
9090 return false
9191 }
92
93 const (
94 _MapCreate = iota
95 _MapLookupElem
96 _MapUpdateElem
97 _MapDeleteElem
98 _MapGetNextKey
99 _ProgLoad
100 _ObjPin
101 _ObjGet
102 _ProgAttach
103 _ProgDetach
104 _ProgTestRun
105 _ProgGetNextID
106 _MapGetNextID
107 _ProgGetFDByID
108 _MapGetFDByID
109 _ObjGetInfoByFD
110 _ProgQuery
111 _RawTracepointOpen
112 _BTFLoad
113 _BTFGetFDByID
114 _TaskFDQuery
115 _MapLookupAndDeleteElem
116 _MapFreeze
117 _BTFGetNextID
118 )
11992
12093 // ProgramType of the eBPF program
12194 type ProgramType uint32
12295
12396 // eBPF program types
12497 const (
125 // Unrecognized program type
12698 UnspecifiedProgram ProgramType = iota
127 // SocketFilter socket or seccomp filter
12899 SocketFilter
129 // Kprobe program
130100 Kprobe
131 // SchedCLS traffic control shaper
132101 SchedCLS
133 // SchedACT routing control shaper
134102 SchedACT
135 // TracePoint program
136103 TracePoint
137 // XDP program
138104 XDP
139 // PerfEvent program
140105 PerfEvent
141 // CGroupSKB program
142106 CGroupSKB
143 // CGroupSock program
144107 CGroupSock
145 // LWTIn program
146108 LWTIn
147 // LWTOut program
148109 LWTOut
149 // LWTXmit program
150110 LWTXmit
151 // SockOps program
152111 SockOps
153 // SkSKB program
154112 SkSKB
155 // CGroupDevice program
156113 CGroupDevice
157 // SkMsg program
158114 SkMsg
159 // RawTracepoint program
160115 RawTracepoint
161 // CGroupSockAddr program
162116 CGroupSockAddr
163 // LWTSeg6Local program
164117 LWTSeg6Local
165 // LircMode2 program
166118 LircMode2
167 // SkReuseport program
168119 SkReuseport
169 // FlowDissector program
170120 FlowDissector
171 // CGroupSysctl program
172121 CGroupSysctl
173 // RawTracepointWritable program
174122 RawTracepointWritable
175 // CGroupSockopt program
176123 CGroupSockopt
177 // Tracing program
178124 Tracing
125 StructOps
126 Extension
127 LSM
128 SkLookup
179129 )
180130
181131 // AttachType of the eBPF program, needed to differentiate allowed context accesses in
213163 AttachTraceRawTp
214164 AttachTraceFEntry
215165 AttachTraceFExit
166 AttachModifyReturn
167 AttachLSMMac
168 AttachTraceIter
169 AttachCgroupInet4GetPeername
170 AttachCgroupInet6GetPeername
171 AttachCgroupInet4GetSockname
172 AttachCgroupInet6GetSockname
173 AttachXDPDevMap
174 AttachCgroupInetSockRelease
175 AttachXDPCPUMap
176 AttachSkLookup
177 AttachXDP
216178 )
217179
218180 // AttachFlags of the eBPF program used in BPF_PROG_ATTACH command
0 // Code generated by "stringer -output types_string.go -type=MapType,ProgramType"; DO NOT EDIT.
0 // Code generated by "stringer -output types_string.go -type=MapType,ProgramType,AttachType"; DO NOT EDIT.
11
22 package ebpf
33
7676 _ = x[RawTracepointWritable-24]
7777 _ = x[CGroupSockopt-25]
7878 _ = x[Tracing-26]
79 _ = x[StructOps-27]
80 _ = x[Extension-28]
81 _ = x[LSM-29]
82 _ = x[SkLookup-30]
7983 }
8084
81 const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracing"
85 const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookup"
8286
83 var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265}
87 var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265, 274, 283, 286, 294}
8488
8589 func (i ProgramType) String() string {
8690 if i >= ProgramType(len(_ProgramType_index)-1) {
8892 }
8993 return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]]
9094 }
95 func _() {
96 // An "invalid array index" compiler error signifies that the constant values have changed.
97 // Re-run the stringer command to generate them again.
98 var x [1]struct{}
99 _ = x[AttachNone-0]
100 _ = x[AttachCGroupInetIngress-0]
101 _ = x[AttachCGroupInetEgress-1]
102 _ = x[AttachCGroupInetSockCreate-2]
103 _ = x[AttachCGroupSockOps-3]
104 _ = x[AttachSkSKBStreamParser-4]
105 _ = x[AttachSkSKBStreamVerdict-5]
106 _ = x[AttachCGroupDevice-6]
107 _ = x[AttachSkMsgVerdict-7]
108 _ = x[AttachCGroupInet4Bind-8]
109 _ = x[AttachCGroupInet6Bind-9]
110 _ = x[AttachCGroupInet4Connect-10]
111 _ = x[AttachCGroupInet6Connect-11]
112 _ = x[AttachCGroupInet4PostBind-12]
113 _ = x[AttachCGroupInet6PostBind-13]
114 _ = x[AttachCGroupUDP4Sendmsg-14]
115 _ = x[AttachCGroupUDP6Sendmsg-15]
116 _ = x[AttachLircMode2-16]
117 _ = x[AttachFlowDissector-17]
118 _ = x[AttachCGroupSysctl-18]
119 _ = x[AttachCGroupUDP4Recvmsg-19]
120 _ = x[AttachCGroupUDP6Recvmsg-20]
121 _ = x[AttachCGroupGetsockopt-21]
122 _ = x[AttachCGroupSetsockopt-22]
123 _ = x[AttachTraceRawTp-23]
124 _ = x[AttachTraceFEntry-24]
125 _ = x[AttachTraceFExit-25]
126 _ = x[AttachModifyReturn-26]
127 _ = x[AttachLSMMac-27]
128 _ = x[AttachTraceIter-28]
129 _ = x[AttachCgroupInet4GetPeername-29]
130 _ = x[AttachCgroupInet6GetPeername-30]
131 _ = x[AttachCgroupInet4GetSockname-31]
132 _ = x[AttachCgroupInet6GetSockname-32]
133 _ = x[AttachXDPDevMap-33]
134 _ = x[AttachCgroupInetSockRelease-34]
135 _ = x[AttachXDPCPUMap-35]
136 _ = x[AttachSkLookup-36]
137 _ = x[AttachXDP-37]
138 }
139
140 const _AttachType_name = "AttachNoneAttachCGroupInetEgressAttachCGroupInetSockCreateAttachCGroupSockOpsAttachSkSKBStreamParserAttachSkSKBStreamVerdictAttachCGroupDeviceAttachSkMsgVerdictAttachCGroupInet4BindAttachCGroupInet6BindAttachCGroupInet4ConnectAttachCGroupInet6ConnectAttachCGroupInet4PostBindAttachCGroupInet6PostBindAttachCGroupUDP4SendmsgAttachCGroupUDP6SendmsgAttachLircMode2AttachFlowDissectorAttachCGroupSysctlAttachCGroupUDP4RecvmsgAttachCGroupUDP6RecvmsgAttachCGroupGetsockoptAttachCGroupSetsockoptAttachTraceRawTpAttachTraceFEntryAttachTraceFExitAttachModifyReturnAttachLSMMacAttachTraceIterAttachCgroupInet4GetPeernameAttachCgroupInet6GetPeernameAttachCgroupInet4GetSocknameAttachCgroupInet6GetSocknameAttachXDPDevMapAttachCgroupInetSockReleaseAttachXDPCPUMapAttachSkLookupAttachXDP"
141
142 var _AttachType_index = [...]uint16{0, 10, 32, 58, 77, 100, 124, 142, 160, 181, 202, 226, 250, 275, 300, 323, 346, 361, 380, 398, 421, 444, 466, 488, 504, 521, 537, 555, 567, 582, 610, 638, 666, 694, 709, 736, 751, 765, 774}
143
144 func (i AttachType) String() string {
145 if i >= AttachType(len(_AttachType_index)-1) {
146 return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")"
147 }
148 return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]]
149 }