1 package ebpf
2
3 import (
4 "errors"
5 "fmt"
6 "math"
7 "os"
8 "path/filepath"
9 "reflect"
10 "sort"
11 "testing"
12 "unsafe"
13
14 "github.com/cilium/ebpf/asm"
15 "github.com/cilium/ebpf/btf"
16 "github.com/cilium/ebpf/internal"
17 "github.com/cilium/ebpf/internal/sys"
18 "github.com/cilium/ebpf/internal/testutils"
19 "github.com/cilium/ebpf/internal/unix"
20
21 qt "github.com/frankban/quicktest"
22 )
23
24 var (
25 spec1 = &MapSpec{
26 Name: "foo",
27 Type: Hash,
28 KeySize: 4,
29 ValueSize: 4,
30 MaxEntries: 1,
31 Pinning: PinByName,
32 }
33 )
34
35 func TestMap(t *testing.T) {
36 m := createArray(t)
37 defer m.Close()
38
39 t.Log(m)
40
41 if err := m.Put(uint32(0), uint32(42)); err != nil {
42 t.Fatal("Can't put:", err)
43 }
44 if err := m.Put(uint32(1), uint32(4242)); err != nil {
45 t.Fatal("Can't put:", err)
46 }
47
48 m2, err := m.Clone()
49 if err != nil {
50 t.Fatal("Can't clone map:", err)
51 }
52 defer m2.Close()
53
54 m.Close()
55 m = m2
56
57 var v uint32
58 if err := m.Lookup(uint32(0), &v); err != nil {
59 t.Fatal("Can't lookup 0:", err)
60 }
61 if v != 42 {
62 t.Error("Want value 42, got", v)
63 }
64
65 var k uint32
66 if err := m.NextKey(uint32(0), &k); err != nil {
67 t.Fatal("Can't get:", err)
68 }
69 if k != 1 {
70 t.Error("Want key 1, got", k)
71 }
72 }
73
74 func TestBatchAPIArray(t *testing.T) {
75 if err := haveBatchAPI(); err != nil {
76 t.Skipf("batch api not available: %v", err)
77 }
78 m, err := NewMap(&MapSpec{
79 Type: Array,
80 KeySize: 4,
81 ValueSize: 4,
82 MaxEntries: 2,
83 })
84 if err != nil {
85 t.Fatal(err)
86 }
87 defer m.Close()
88
89 var (
90 nextKey uint32
91 keys = []uint32{0, 1}
92 values = []uint32{42, 4242}
93 lookupKeys = make([]uint32, 2)
94 lookupValues = make([]uint32, 2)
95 deleteKeys = make([]uint32, 2)
96 deleteValues = make([]uint32, 2)
97 )
98
99 count, err := m.BatchUpdate(keys, values, nil)
100 if err != nil {
101 t.Fatalf("BatchUpdate: %v", err)
102 }
103 if count != len(keys) {
104 t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys))
105 }
106
107 var v uint32
108 if err := m.Lookup(uint32(0), &v); err != nil {
109 t.Fatal("Can't lookup 0:", err)
110 }
111 if v != 42 {
112 t.Error("Want value 42, got", v)
113 }
114
115 count, err = m.BatchLookup(nil, &nextKey, lookupKeys, lookupValues, nil)
116 if err != nil {
117 t.Fatalf("BatchLookup: %v", err)
118 }
119 if count != len(lookupKeys) {
120 t.Fatalf("BatchLookup: returned %d results, expected %d", count, len(lookupKeys))
121 }
122 if nextKey != lookupKeys[1] {
123 t.Fatalf("BatchLookup: expected nextKey, %d, to be the same as the lastKey returned, %d", nextKey, lookupKeys[1])
124 }
125 if !reflect.DeepEqual(keys, lookupKeys) {
126 t.Errorf("BatchUpdate and BatchLookup keys disagree: %v %v", keys, lookupKeys)
127 }
128 if !reflect.DeepEqual(values, lookupValues) {
129 t.Errorf("BatchUpdate and BatchLookup values disagree: %v %v", values, lookupValues)
130 }
131
132 _, err = m.BatchLookupAndDelete(nil, &nextKey, deleteKeys, deleteValues, nil)
133 if !errors.Is(err, ErrNotSupported) {
134 t.Fatalf("BatchLookUpDelete: expected error %v, but got %v", ErrNotSupported, err)
135 }
136 }
137
138 func TestBatchAPIHash(t *testing.T) {
139 if err := haveBatchAPI(); err != nil {
140 t.Skipf("batch api not available: %v", err)
141 }
142 m, err := NewMap(&MapSpec{
143 Type: Hash,
144 KeySize: 4,
145 ValueSize: 4,
146 MaxEntries: 10,
147 })
148 if err != nil {
149 t.Fatal(err)
150 }
151 defer m.Close()
152
153 var (
154 nextKey uint32
155 keys = []uint32{0, 1}
156 values = []uint32{42, 4242}
157 lookupKeys = make([]uint32, 2)
158 lookupValues = make([]uint32, 2)
159 deleteKeys = make([]uint32, 2)
160 deleteValues = make([]uint32, 2)
161 )
162
163 count, err := m.BatchUpdate(keys, values, nil)
164 if err != nil {
165 t.Fatalf("BatchUpdate: %v", err)
166 }
167 if count != len(keys) {
168 t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys))
169 }
170
171 var v uint32
172 if err := m.Lookup(uint32(0), &v); err != nil {
173 t.Fatal("Can't lookup 0:", err)
174 }
175 if v != 42 {
176 t.Error("Want value 42, got", v)
177 }
178
179 count, err = m.BatchLookup(nil, &nextKey, lookupKeys, lookupValues, nil)
180 if !errors.Is(err, ErrKeyNotExist) {
181 t.Fatalf("BatchLookup: expected %v got %v", ErrKeyNotExist, err)
182 }
183 if count != len(lookupKeys) {
184 t.Fatalf("BatchLookup: returned %d results, expected %d", count, len(lookupKeys))
185 }
186 sort.Slice(lookupKeys, func(i, j int) bool { return lookupKeys[i] < lookupKeys[j] })
187 if !reflect.DeepEqual(keys, lookupKeys) {
188 t.Errorf("BatchUpdate and BatchLookup keys disagree: %v %v", keys, lookupKeys)
189 }
190 sort.Slice(lookupValues, func(i, j int) bool { return lookupValues[i] < lookupValues[j] })
191 if !reflect.DeepEqual(values, lookupValues) {
192 t.Errorf("BatchUpdate and BatchLookup values disagree: %v %v", values, lookupValues)
193 }
194
195 count, err = m.BatchLookupAndDelete(nil, &nextKey, deleteKeys, deleteValues, nil)
196 if !errors.Is(err, ErrKeyNotExist) {
197 t.Fatalf("BatchLookupAndDelete: expected %v got %v", ErrKeyNotExist, err)
198 }
199 if count != len(deleteKeys) {
200 t.Fatalf("BatchLookupAndDelete: returned %d results, expected %d", count, len(deleteKeys))
201 }
202 sort.Slice(deleteKeys, func(i, j int) bool { return deleteKeys[i] < deleteKeys[j] })
203 if !reflect.DeepEqual(keys, deleteKeys) {
204 t.Errorf("BatchUpdate and BatchLookupAndDelete keys disagree: %v %v", keys, deleteKeys)
205 }
206 sort.Slice(deleteValues, func(i, j int) bool { return deleteValues[i] < deleteValues[j] })
207 if !reflect.DeepEqual(values, deleteValues) {
208 t.Errorf("BatchUpdate and BatchLookupAndDelete values disagree: %v %v", values, deleteValues)
209 }
210
211 if err := m.Lookup(uint32(0), &v); !errors.Is(err, ErrKeyNotExist) {
212 t.Fatalf("Lookup should have failed with error, %v, instead error is %v", ErrKeyNotExist, err)
213 }
214 }
215
216 func TestBatchAPIMapDelete(t *testing.T) {
217 if err := haveBatchAPI(); err != nil {
218 t.Skipf("batch api not available: %v", err)
219 }
220 m, err := NewMap(&MapSpec{
221 Type: Hash,
222 KeySize: 4,
223 ValueSize: 4,
224 MaxEntries: 10,
225 })
226 if err != nil {
227 t.Fatal(err)
228 }
229 defer m.Close()
230
231 var (
232 keys = []uint32{0, 1}
233 values = []uint32{42, 4242}
234 )
235
236 count, err := m.BatchUpdate(keys, values, nil)
237 if err != nil {
238 t.Fatalf("BatchUpdate: %v", err)
239 }
240 if count != len(keys) {
241 t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys))
242 }
243
244 var v uint32
245 if err := m.Lookup(uint32(0), &v); err != nil {
246 t.Fatal("Can't lookup 0:", err)
247 }
248 if v != 42 {
249 t.Error("Want value 42, got", v)
250 }
251
252 count, err = m.BatchDelete(keys, nil)
253 if err != nil {
254 t.Fatalf("BatchDelete: %v", err)
255 }
256 if count != len(keys) {
257 t.Fatalf("BatchDelete: expected %d deletions got %d", len(keys), count)
258 }
259
260 if err := m.Lookup(uint32(0), &v); !errors.Is(err, ErrKeyNotExist) {
261 t.Fatalf("Lookup should have failed with error, %v, instead error is %v", ErrKeyNotExist, err)
262 }
263 }
264
265 func TestMapClose(t *testing.T) {
266 m := createArray(t)
267
268 if err := m.Close(); err != nil {
269 t.Fatal("Can't close map:", err)
270 }
271
272 if err := m.Put(uint32(0), uint32(42)); !errors.Is(err, sys.ErrClosedFd) {
273 t.Fatal("Put doesn't check for closed fd", err)
274 }
275
276 if _, err := m.LookupBytes(uint32(0)); !errors.Is(err, sys.ErrClosedFd) {
277 t.Fatal("Get doesn't check for closed fd", err)
278 }
279 }
280
281 func TestBatchMapWithLock(t *testing.T) {
282 testutils.SkipOnOldKernel(t, "5.13", "MAP BATCH BPF_F_LOCK")
283 testutils.Files(t, testutils.Glob(t, "./testdata/map_spin_lock-*.elf"), func(t *testing.T, file string) {
284 spec, err := LoadCollectionSpec(file)
285 if err != nil {
286 t.Fatal("Can't parse ELF:", err)
287 }
288 if spec.ByteOrder != internal.NativeEndian {
289 return
290 }
291
292 coll, err := NewCollection(spec)
293 if err != nil {
294 t.Fatal("Can't parse ELF:", err)
295 }
296 defer coll.Close()
297
298 type spinLockValue struct {
299 Cnt uint32
300 Padding uint32
301 }
302
303 m, ok := coll.Maps["spin_lock_map"]
304 if !ok {
305 t.Fatal(err)
306 }
307
308 keys := []uint32{0, 1}
309 values := []spinLockValue{{Cnt: 42}, {Cnt: 4242}}
310 count, err := m.BatchUpdate(keys, values, &BatchOptions{ElemFlags: uint64(UpdateLock)})
311 if err != nil {
312 t.Fatalf("BatchUpdate: %v", err)
313 }
314 if count != len(keys) {
315 t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys))
316 }
317
318 nextKey := uint32(0)
319 lookupKeys := make([]uint32, 2)
320 lookupValues := make([]spinLockValue, 2)
321 count, err = m.BatchLookup(nil, &nextKey, lookupKeys, lookupValues, &BatchOptions{ElemFlags: uint64(LookupLock)})
322 if !errors.Is(err, ErrKeyNotExist) {
323 t.Fatalf("BatchLookup: %v", err)
324 }
325 if count != 2 {
326 t.Fatalf("BatchLookup: expected two keys, got %d", count)
327 }
328
329 nextKey = uint32(0)
330 deleteKeys := []uint32{0, 1}
331 deleteValues := make([]spinLockValue, 2)
332 count, err = m.BatchLookupAndDelete(nil, &nextKey, deleteKeys, deleteValues, nil)
333 if !errors.Is(err, ErrKeyNotExist) {
334 t.Fatalf("BatchLookupAndDelete: %v", err)
335 }
336 if count != 2 {
337 t.Fatalf("BatchLookupAndDelete: expected two keys, got %d", count)
338 }
339 })
340 }
341
342 func TestMapWithLock(t *testing.T) {
343 testutils.SkipOnOldKernel(t, "5.13", "MAP BPF_F_LOCK")
344 testutils.Files(t, testutils.Glob(t, "./testdata/map_spin_lock-*.elf"), func(t *testing.T, file string) {
345 spec, err := LoadCollectionSpec(file)
346 if err != nil {
347 t.Fatal("Can't parse ELF:", err)
348 }
349 if spec.ByteOrder != internal.NativeEndian {
350 return
351 }
352
353 coll, err := NewCollection(spec)
354 if err != nil {
355 t.Fatal("Can't parse ELF:", err)
356 }
357 defer coll.Close()
358
359 type spinLockValue struct {
360 Cnt uint32
361 Padding uint32
362 }
363
364 m, ok := coll.Maps["spin_lock_map"]
365 if !ok {
366 t.Fatal(err)
367 }
368
369 key := uint32(1)
370 value := spinLockValue{Cnt: 5}
371 err = m.Update(key, value, UpdateLock)
372 if err != nil {
373 t.Fatal(err)
374 }
375
376 value.Cnt = 0
377 err = m.LookupWithFlags(&key, &value, LookupLock)
378 if err != nil {
379 t.Fatal(err)
380 }
381 if value.Cnt != 5 {
382 t.Fatalf("Want value 5, got %d", value.Cnt)
383 }
384
385 value.Cnt = 0
386 err = m.LookupAndDeleteWithFlags(&key, &value, LookupLock)
387 if err != nil {
388 t.Fatal(err)
389 }
390 if value.Cnt != 5 {
391 t.Fatalf("Want value 5, got %d", value.Cnt)
392 }
393
394 err = m.LookupWithFlags(&key, &value, LookupLock)
395 if err != nil && !errors.Is(err, ErrKeyNotExist) {
396 t.Fatal(err)
397 }
398
399 })
400 }
401
402 func TestMapCloneNil(t *testing.T) {
403 m, err := (*Map)(nil).Clone()
404 if err != nil {
405 t.Fatal(err)
406 }
407
408 if m != nil {
409 t.Fatal("Cloning a nil map doesn't return nil")
410 }
411 }
412
413 func TestMapPin(t *testing.T) {
414 m := createArray(t)
415 c := qt.New(t)
416 defer m.Close()
417
418 if err := m.Put(uint32(0), uint32(42)); err != nil {
419 t.Fatal("Can't put:", err)
420 }
421
422 tmp := testutils.TempBPFFS(t)
423 path := filepath.Join(tmp, "map")
424
425 if err := m.Pin(path); err != nil {
426 testutils.SkipIfNotSupported(t, err)
427 t.Fatal(err)
428 }
429
430 pinned := m.IsPinned()
431 c.Assert(pinned, qt.IsTrue)
432
433 m.Close()
434
435 m, err := LoadPinnedMap(path, nil)
436 testutils.SkipIfNotSupported(t, err)
437 if err != nil {
438 t.Fatal(err)
439 }
440 defer m.Close()
441
442 var v uint32
443 if err := m.Lookup(uint32(0), &v); err != nil {
444 t.Fatal("Can't lookup 0:", err)
445 }
446 if v != 42 {
447 t.Error("Want value 42, got", v)
448 }
449 }
450
451 func TestNestedMapPin(t *testing.T) {
452 m, err := NewMap(&MapSpec{
453 Type: ArrayOfMaps,
454 KeySize: 4,
455 ValueSize: 4,
456 MaxEntries: 2,
457 InnerMap: &MapSpec{
458 Type: Array,
459 KeySize: 4,
460 ValueSize: 4,
461 MaxEntries: 1,
462 },
463 })
464 testutils.SkipIfNotSupported(t, err)
465 if err != nil {
466 t.Fatal(err)
467 }
468 defer m.Close()
469
470 tmp, err := os.MkdirTemp("/sys/fs/bpf", "ebpf-test")
471 if err != nil {
472 t.Fatal(err)
473 }
474 defer os.RemoveAll(tmp)
475
476 path := filepath.Join(tmp, "nested")
477 if err := m.Pin(path); err != nil {
478 t.Fatal(err)
479 }
480 m.Close()
481
482 m, err = LoadPinnedMap(path, nil)
483 testutils.SkipIfNotSupported(t, err)
484 if err != nil {
485 t.Fatal(err)
486 }
487 defer m.Close()
488 }
489
490 func TestNestedMapPinNested(t *testing.T) {
491 if _, err := NewMap(&MapSpec{
492 Type: ArrayOfMaps,
493 KeySize: 4,
494 ValueSize: 4,
495 MaxEntries: 2,
496 InnerMap: &MapSpec{
497 Name: "inner",
498 Type: Array,
499 KeySize: 4,
500 ValueSize: 4,
501 MaxEntries: 1,
502 Pinning: PinByName,
503 },
504 }); err == nil {
505 t.Error("Inner maps should not be pinnable")
506 }
507 }
508
509 func TestMapPinMultiple(t *testing.T) {
510 testutils.SkipOnOldKernel(t, "4.9", "atomic re-pinning was introduced in 4.9 series")
511
512 tmp := testutils.TempBPFFS(t)
513 c := qt.New(t)
514
515 spec := spec1.Copy()
516
517 m1, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp})
518 if err != nil {
519 t.Fatal("Can't create map:", err)
520 }
521 defer m1.Close()
522 pinned := m1.IsPinned()
523 c.Assert(pinned, qt.IsTrue)
524
525 newPath := filepath.Join(tmp, "bar")
526 err = m1.Pin(newPath)
527 testutils.SkipIfNotSupported(t, err)
528 c.Assert(err, qt.IsNil)
529 oldPath := filepath.Join(tmp, spec.Name)
530 if _, err := os.Stat(oldPath); err == nil {
531 t.Fatal("Previous pinned map path still exists:", err)
532 }
533 m2, err := LoadPinnedMap(newPath, nil)
534 c.Assert(err, qt.IsNil)
535 pinned = m2.IsPinned()
536 c.Assert(pinned, qt.IsTrue)
537 defer m2.Close()
538 }
539
540 func TestMapPinWithEmptyPath(t *testing.T) {
541 m := createArray(t)
542 c := qt.New(t)
543 defer m.Close()
544
545 err := m.Pin("")
546
547 c.Assert(err, qt.Not(qt.IsNil))
548 }
549
550 func TestMapPinFailReplace(t *testing.T) {
551 tmp := testutils.TempBPFFS(t)
552 c := qt.New(t)
553 spec := spec1.Copy()
554 spec2 := spec1.Copy()
555 spec2.Name = spec1.Name + "bar"
556
557 m, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp})
558 if err != nil {
559 t.Fatal("Failed to create map:", err)
560 }
561 defer m.Close()
562 m2, err := NewMapWithOptions(spec2, MapOptions{PinPath: tmp})
563 if err != nil {
564 t.Fatal("Failed to create map2:", err)
565 }
566 defer m2.Close()
567 c.Assert(m.IsPinned(), qt.IsTrue)
568 newPath := filepath.Join(tmp, spec2.Name)
569
570 c.Assert(m.Pin(newPath), qt.Not(qt.IsNil), qt.Commentf("Pin didn't"+
571 " fail new path from replacing an existing path"))
572 }
573
574 func TestMapUnpin(t *testing.T) {
575 tmp := testutils.TempBPFFS(t)
576 c := qt.New(t)
577 spec := spec1.Copy()
578
579 m, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp})
580 if err != nil {
581 t.Fatal("Failed to create map:", err)
582 }
583 defer m.Close()
584
585 pinned := m.IsPinned()
586 c.Assert(pinned, qt.IsTrue)
587 path := filepath.Join(tmp, spec.Name)
588 m2, err := LoadPinnedMap(path, nil)
589 testutils.SkipIfNotSupported(t, err)
590 c.Assert(err, qt.IsNil)
591 defer m2.Close()
592
593 if err = m.Unpin(); err != nil {
594 t.Fatal("Failed to unpin map:", err)
595 }
596 if _, err := os.Stat(path); err == nil {
597 t.Fatal("Pinned map path still exists after unpinning:", err)
598 }
599 }
600
601 func TestMapLoadPinned(t *testing.T) {
602 tmp := testutils.TempBPFFS(t)
603 c := qt.New(t)
604
605 spec := spec1.Copy()
606
607 m1, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp})
608 c.Assert(err, qt.IsNil)
609 defer m1.Close()
610 pinned := m1.IsPinned()
611 c.Assert(pinned, qt.IsTrue)
612
613 path := filepath.Join(tmp, spec.Name)
614 m2, err := LoadPinnedMap(path, nil)
615 testutils.SkipIfNotSupported(t, err)
616 c.Assert(err, qt.IsNil)
617 defer m2.Close()
618 pinned = m2.IsPinned()
619 c.Assert(pinned, qt.IsTrue)
620 }
621
622 func TestMapLoadPinnedUnpin(t *testing.T) {
623 tmp := testutils.TempBPFFS(t)
624 c := qt.New(t)
625
626 spec := spec1.Copy()
627
628 m1, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp})
629 c.Assert(err, qt.IsNil)
630 defer m1.Close()
631 pinned := m1.IsPinned()
632 c.Assert(pinned, qt.IsTrue)
633
634 path := filepath.Join(tmp, spec.Name)
635 m2, err := LoadPinnedMap(path, nil)
636 testutils.SkipIfNotSupported(t, err)
637 c.Assert(err, qt.IsNil)
638 defer m2.Close()
639 err = m1.Unpin()
640 c.Assert(err, qt.IsNil)
641 err = m2.Unpin()
642 c.Assert(err, qt.IsNil)
643 }
644
645 func TestMapLoadPinnedWithOptions(t *testing.T) {
646
647 testutils.SkipOnOldKernel(t, "4.15", "file_flags in BPF_OBJ_GET")
648
649 array := createArray(t)
650 defer array.Close()
651
652 tmp := testutils.TempBPFFS(t)
653
654 path := filepath.Join(tmp, "map")
655 if err := array.Pin(path); err != nil {
656 t.Fatal(err)
657 }
658 if err := array.Put(uint32(0), uint32(123)); err != nil {
659 t.Fatal(err)
660 }
661 array.Close()
662
663 t.Run("read-only", func(t *testing.T) {
664 array, err := LoadPinnedMap(path, &LoadPinOptions{
665 ReadOnly: true,
666 })
667 testutils.SkipIfNotSupported(t, err)
668 if err != nil {
669 t.Fatal("Can't load map:", err)
670 }
671 defer array.Close()
672
673 if err := array.Put(uint32(0), uint32(1)); !errors.Is(err, unix.EPERM) {
674 t.Fatal("Expected EPERM from Put, got", err)
675 }
676 })
677
678 t.Run("write-only", func(t *testing.T) {
679 array, err := LoadPinnedMap(path, &LoadPinOptions{
680 WriteOnly: true,
681 })
682 testutils.SkipIfNotSupported(t, err)
683 if err != nil {
684 t.Fatal("Can't load map:", err)
685 }
686 defer array.Close()
687
688 var value uint32
689 if err := array.Lookup(uint32(0), &value); !errors.Is(err, unix.EPERM) {
690 t.Fatal("Expected EPERM from Lookup, got", err)
691 }
692 })
693 }
694
695 func TestMapPinFlags(t *testing.T) {
696 tmp := testutils.TempBPFFS(t)
697
698 spec := &MapSpec{
699 Name: "map",
700 Type: Array,
701 KeySize: 4,
702 ValueSize: 4,
703 MaxEntries: 1,
704 Pinning: PinByName,
705 }
706
707 m, err := NewMapWithOptions(spec, MapOptions{
708 PinPath: tmp,
709 })
710 qt.Assert(t, err, qt.IsNil)
711 m.Close()
712
713 _, err = NewMapWithOptions(spec, MapOptions{
714 PinPath: tmp,
715 LoadPinOptions: LoadPinOptions{
716 Flags: math.MaxUint32,
717 },
718 })
719 if !errors.Is(err, unix.EINVAL) {
720 t.Fatal("Invalid flags should trigger EINVAL:", err)
721 }
722 }
723
724 func createArray(t *testing.T) *Map {
725 t.Helper()
726
727 m, err := NewMap(&MapSpec{
728 Type: Array,
729 KeySize: 4,
730 ValueSize: 4,
731 MaxEntries: 2,
732 })
733 if err != nil {
734 t.Fatal(err)
735 }
736 return m
737 }
738
739 func TestMapQueue(t *testing.T) {
740 testutils.SkipOnOldKernel(t, "4.20", "map type queue")
741
742 m, err := NewMap(&MapSpec{
743 Type: Queue,
744 ValueSize: 4,
745 MaxEntries: 2,
746 })
747 if err != nil {
748 t.Fatal(err)
749 }
750 defer m.Close()
751
752 for _, v := range []uint32{42, 4242} {
753 if err := m.Put(nil, v); err != nil {
754 t.Fatalf("Can't put %d: %s", v, err)
755 }
756 }
757
758 var v uint32
759 if err := m.LookupAndDelete(nil, &v); err != nil {
760 t.Fatal("Can't lookup and delete element:", err)
761 }
762 if v != 42 {
763 t.Error("Want value 42, got", v)
764 }
765
766 v = 0
767 if err := m.LookupAndDelete(nil, unsafe.Pointer(&v)); err != nil {
768 t.Fatal("Can't lookup and delete element using unsafe.Pointer:", err)
769 }
770 if v != 4242 {
771 t.Error("Want value 4242, got", v)
772 }
773
774 if err := m.LookupAndDelete(nil, &v); !errors.Is(err, ErrKeyNotExist) {
775 t.Fatal("Lookup and delete on empty Queue:", err)
776 }
777 }
778
779 func TestMapInMap(t *testing.T) {
780 for _, typ := range []MapType{ArrayOfMaps, HashOfMaps} {
781 t.Run(typ.String(), func(t *testing.T) {
782 spec := &MapSpec{
783 Type: typ,
784 KeySize: 4,
785 MaxEntries: 2,
786 InnerMap: &MapSpec{
787 Type: Array,
788 KeySize: 4,
789 ValueSize: 4,
790 MaxEntries: 2,
791 },
792 }
793
794 inner, err := NewMap(spec.InnerMap)
795 if err != nil {
796 t.Fatal(err)
797 }
798 if err := inner.Put(uint32(1), uint32(4242)); err != nil {
799 t.Fatal(err)
800 }
801 defer inner.Close()
802
803 outer, err := NewMap(spec)
804 testutils.SkipIfNotSupported(t, err)
805 if err != nil {
806 t.Fatal(err)
807 }
808 defer outer.Close()
809
810 if err := outer.Put(uint32(0), inner); err != nil {
811 t.Fatal("Can't put inner map:", err)
812 }
813
814 var inner2 *Map
815 if err := outer.Lookup(uint32(0), &inner2); err != nil {
816 t.Fatal("Can't lookup 0:", err)
817 }
818 defer inner2.Close()
819
820 var v uint32
821 if err := inner2.Lookup(uint32(1), &v); err != nil {
822 t.Fatal("Can't lookup 1 in inner2:", err)
823 }
824
825 if v != 4242 {
826 t.Error("Expected value 4242, got", v)
827 }
828
829 inner2.Close()
830
831
832 if err := inner.Lookup(uint32(1), &v); err != nil {
833 t.Fatal("Can't lookup 1 in inner:", err)
834 }
835
836 if v != 4242 {
837 t.Error("Expected value 4242, got", v)
838 }
839 })
840 }
841 }
842
843 func TestNewMapInMapFromFD(t *testing.T) {
844 nested, err := NewMap(&MapSpec{
845 Type: ArrayOfMaps,
846 KeySize: 4,
847 MaxEntries: 2,
848 InnerMap: &MapSpec{
849 Type: Array,
850 KeySize: 4,
851 ValueSize: 4,
852 MaxEntries: 2,
853 },
854 })
855 testutils.SkipIfNotSupported(t, err)
856 if err != nil {
857 t.Fatal(err)
858 }
859 defer nested.Close()
860
861
862 another, err := NewMapFromFD(nested.FD())
863 if err != nil {
864 t.Fatal("Can't create a new nested map from an FD")
865 }
866 another.Close()
867 }
868
869 func TestPerfEventArray(t *testing.T) {
870 specs := []*MapSpec{
871 {Type: PerfEventArray},
872 {Type: PerfEventArray, KeySize: 4},
873 {Type: PerfEventArray, ValueSize: 4},
874 }
875
876 for _, spec := range specs {
877 m, err := NewMap(spec)
878 if err != nil {
879 t.Errorf("Can't create perf event array from %v: %s", spec, err)
880 } else {
881 m.Close()
882 }
883 }
884 }
885
886 func createMapInMap(t *testing.T, typ MapType) *Map {
887 t.Helper()
888
889 spec := &MapSpec{
890 Type: typ,
891 KeySize: 4,
892 MaxEntries: 2,
893 InnerMap: &MapSpec{
894 Type: Array,
895 KeySize: 4,
896 ValueSize: 4,
897 MaxEntries: 2,
898 },
899 }
900
901 m, err := NewMap(spec)
902 testutils.SkipIfNotSupported(t, err)
903 if err != nil {
904 t.Fatal(err)
905 }
906 return m
907 }
908
909 func TestMapInMapValueSize(t *testing.T) {
910 spec := &MapSpec{
911 Type: ArrayOfMaps,
912 KeySize: 4,
913 ValueSize: 0,
914 MaxEntries: 2,
915 InnerMap: &MapSpec{
916 Type: Array,
917 KeySize: 4,
918 ValueSize: 4,
919 MaxEntries: 2,
920 },
921 }
922
923 m, err := NewMap(spec)
924 testutils.SkipIfNotSupported(t, err)
925 if err != nil {
926 t.Fatal(err)
927 }
928 m.Close()
929
930 spec.ValueSize = 4
931 m, err = NewMap(spec)
932 if err != nil {
933 t.Fatal(err)
934 }
935 m.Close()
936
937 spec.ValueSize = 1
938 if _, err := NewMap(spec); err == nil {
939 t.Fatal("Expected an error")
940 }
941 }
942
943 func TestIterateEmptyMap(t *testing.T) {
944 makeMap := func(t *testing.T, mapType MapType) *Map {
945 m, err := NewMap(&MapSpec{
946 Type: mapType,
947 KeySize: 4,
948 ValueSize: 8,
949 MaxEntries: 2,
950 })
951 if errors.Is(err, unix.EINVAL) {
952 t.Skip(mapType, "is not supported")
953 }
954 if err != nil {
955 t.Fatal("Can't create map:", err)
956 }
957 t.Cleanup(func() { m.Close() })
958 return m
959 }
960
961 for _, mapType := range []MapType{
962 Hash,
963 SockHash,
964 } {
965 t.Run(mapType.String(), func(t *testing.T) {
966 m := makeMap(t, mapType)
967 entries := m.Iterate()
968
969 var key string
970 var value uint32
971 if entries.Next(&key, &value) != false {
972 t.Error("Empty hash should not be iterable")
973 }
974 if err := entries.Err(); err != nil {
975 t.Error("Empty hash shouldn't return an error:", err)
976 }
977 })
978 }
979
980 for _, mapType := range []MapType{
981 Array,
982 SockMap,
983 } {
984 t.Run(mapType.String(), func(t *testing.T) {
985 m := makeMap(t, mapType)
986 entries := m.Iterate()
987 var key string
988 var value uint32
989 for entries.Next(&key, &value) {
990
991 }
992 if err := entries.Err(); err != nil {
993 t.Error("Empty array shouldn't return an error:", err)
994 }
995 })
996 }
997 }
998
999 func TestMapIterate(t *testing.T) {
1000 hash, err := NewMap(&MapSpec{
1001 Type: Hash,
1002 KeySize: 5,
1003 ValueSize: 4,
1004 MaxEntries: 2,
1005 })
1006 if err != nil {
1007 t.Fatal(err)
1008 }
1009 defer hash.Close()
1010
1011 if err := hash.Put("hello", uint32(21)); err != nil {
1012 t.Fatal(err)
1013 }
1014
1015 if err := hash.Put("world", uint32(42)); err != nil {
1016 t.Fatal(err)
1017 }
1018
1019 var key string
1020 var value uint32
1021 var keys []string
1022
1023 entries := hash.Iterate()
1024 for entries.Next(&key, &value) {
1025 keys = append(keys, key)
1026 }
1027
1028 if err := entries.Err(); err != nil {
1029 t.Fatal(err)
1030 }
1031
1032 sort.Strings(keys)
1033
1034 if n := len(keys); n != 2 {
1035 t.Fatal("Expected to get 2 keys, have", n)
1036 }
1037 if keys[0] != "hello" {
1038 t.Error("Expected index 0 to be hello, got", keys[0])
1039 }
1040 if keys[1] != "world" {
1041 t.Error("Expected index 1 to be hello, got", keys[1])
1042 }
1043 }
1044
1045 func TestMapIterateHashKeyOneByteFull(t *testing.T) {
1046 hash, err := NewMap(&MapSpec{
1047 Type: Hash,
1048 KeySize: 1,
1049 ValueSize: 1,
1050 MaxEntries: 256,
1051 })
1052 if err != nil {
1053 t.Fatal(err)
1054 }
1055 defer hash.Close()
1056
1057 for i := 0; i < int(hash.MaxEntries()); i++ {
1058 if err := hash.Put(uint8(i), uint8(i)); err != nil {
1059 t.Fatal(err)
1060 }
1061 }
1062 var key uint8
1063 var value uint8
1064 var keys int
1065
1066 entries := hash.Iterate()
1067 for entries.Next(&key, &value) {
1068 if key != value {
1069 t.Fatalf("Expected key == value, got key %v value %v", key, value)
1070 }
1071 keys++
1072 }
1073
1074 if err := entries.Err(); err != nil {
1075 t.Fatal(err)
1076 }
1077
1078 if keys != int(hash.MaxEntries()) {
1079 t.Fatalf("Expected to get %d keys, have %d", hash.MaxEntries(), keys)
1080 }
1081 }
1082
1083 func TestMapGuessNonExistentKey(t *testing.T) {
1084 tests := []struct {
1085 name string
1086 mapType MapType
1087 keys []uint32
1088 }{
1089 {
1090 "empty", Hash, []uint32{},
1091 },
1092 {
1093 "all zero key", Hash, []uint32{0},
1094 },
1095 {
1096 "all ones key", Hash, []uint32{math.MaxUint32},
1097 },
1098 {
1099 "alternating bits key", Hash, []uint32{0x5555_5555},
1100 },
1101 {
1102 "all special patterns", Hash, []uint32{0, math.MaxUint32, 0x5555_5555},
1103 },
1104 {
1105 "empty", Array, []uint32{},
1106 },
1107 {
1108 "all zero key", Array, []uint32{0},
1109 },
1110 {
1111 "full", Array, []uint32{0, 1},
1112 },
1113 }
1114
1115 for _, tt := range tests {
1116 t.Run(fmt.Sprintf("%s: %s", tt.mapType, tt.name), func(t *testing.T) {
1117 maxEntries := uint32(len(tt.keys))
1118 if maxEntries == 0 {
1119 maxEntries = 1
1120 }
1121
1122 m, err := NewMap(&MapSpec{
1123 Type: tt.mapType,
1124 KeySize: 4,
1125 ValueSize: 4,
1126 MaxEntries: maxEntries,
1127 })
1128 if err != nil {
1129 t.Fatal(err)
1130 }
1131 defer m.Close()
1132
1133 for _, key := range tt.keys {
1134 if err := m.Put(key, key); err != nil {
1135 t.Fatal(err)
1136 }
1137 }
1138
1139 guess, err := m.guessNonExistentKey()
1140 if err != nil {
1141 t.Fatal(err)
1142 }
1143
1144 if len(guess) != int(m.keySize) {
1145 t.Fatal("Guessed key has wrong size")
1146 }
1147
1148 var value uint32
1149 if err := m.Lookup(guess, &value); !errors.Is(err, unix.ENOENT) {
1150 t.Fatal("Doesn't return ENOENT:", err)
1151 }
1152 })
1153 }
1154
1155 t.Run("Hash: full", func(t *testing.T) {
1156 const n = math.MaxUint8 + 1
1157
1158 hash, err := NewMap(&MapSpec{
1159 Type: Hash,
1160 KeySize: 1,
1161 ValueSize: 1,
1162 MaxEntries: n,
1163 })
1164 if err != nil {
1165 t.Fatal(err)
1166 }
1167 defer hash.Close()
1168
1169 for i := 0; i < n; i++ {
1170 if err := hash.Put(uint8(i), uint8(i)); err != nil {
1171 t.Fatal(err)
1172 }
1173 }
1174
1175 _, err = hash.guessNonExistentKey()
1176 if err == nil {
1177 t.Fatal("guessNonExistentKey doesn't return error on full hash table")
1178 }
1179 })
1180 }
1181
1182 func TestNotExist(t *testing.T) {
1183 hash := createHash()
1184 defer hash.Close()
1185
1186 var tmp uint32
1187 err := hash.Lookup("hello", &tmp)
1188 if !errors.Is(err, ErrKeyNotExist) {
1189 t.Error("Lookup doesn't return ErrKeyNotExist")
1190 }
1191
1192 buf, err := hash.LookupBytes("hello")
1193 if err != nil {
1194 t.Error("Looking up non-existent key return an error:", err)
1195 }
1196 if buf != nil {
1197 t.Error("LookupBytes returns non-nil buffer for non-existent key")
1198 }
1199
1200 if err := hash.Delete("hello"); !errors.Is(err, ErrKeyNotExist) {
1201 t.Error("Deleting unknown key doesn't return ErrKeyNotExist", err)
1202 }
1203
1204 var k = []byte{1, 2, 3, 4, 5}
1205 if err := hash.NextKey(&k, &tmp); !errors.Is(err, ErrKeyNotExist) {
1206 t.Error("Looking up next key in empty map doesn't return a non-existing error", err)
1207 }
1208
1209 if err := hash.NextKey(nil, &tmp); !errors.Is(err, ErrKeyNotExist) {
1210 t.Error("Looking up next key in empty map doesn't return a non-existing error", err)
1211 }
1212 }
1213
1214 func TestExist(t *testing.T) {
1215 hash := createHash()
1216 defer hash.Close()
1217
1218 if err := hash.Put("hello", uint32(21)); err != nil {
1219 t.Errorf("Failed to put key/value pair into hash: %v", err)
1220 }
1221
1222 if err := hash.Update("hello", uint32(42), UpdateNoExist); !errors.Is(err, ErrKeyExist) {
1223 t.Error("Updating existing key doesn't return ErrKeyExist")
1224 }
1225 }
1226
1227 func TestIterateMapInMap(t *testing.T) {
1228 const idx = uint32(1)
1229
1230 parent := createMapInMap(t, ArrayOfMaps)
1231 defer parent.Close()
1232
1233 a := createArray(t)
1234 defer a.Close()
1235
1236 if err := parent.Put(idx, a); err != nil {
1237 t.Fatal(err)
1238 }
1239
1240 var (
1241 key uint32
1242 m *Map
1243 entries = parent.Iterate()
1244 )
1245
1246 if !entries.Next(&key, &m) {
1247 t.Fatal("Iterator encountered error:", entries.Err())
1248 }
1249 m.Close()
1250
1251 if key != 1 {
1252 t.Error("Iterator didn't skip first entry")
1253 }
1254
1255 if m == nil {
1256 t.Fatal("Map is nil")
1257 }
1258 }
1259
1260 func TestPerCPUMarshaling(t *testing.T) {
1261 for _, typ := range []MapType{PerCPUHash, PerCPUArray, LRUCPUHash} {
1262 t.Run(typ.String(), func(t *testing.T) {
1263 numCPU, err := internal.PossibleCPUs()
1264 if err != nil {
1265 t.Fatal(err)
1266 }
1267 if numCPU < 2 {
1268 t.Skip("Test requires at least two CPUs")
1269 }
1270 if typ == PerCPUHash || typ == PerCPUArray {
1271 testutils.SkipOnOldKernel(t, "4.6", "per-CPU hash and array")
1272 }
1273 if typ == LRUCPUHash {
1274 testutils.SkipOnOldKernel(t, "4.10", "LRU per-CPU hash")
1275 }
1276
1277 arr, err := NewMap(&MapSpec{
1278 Type: typ,
1279 KeySize: 4,
1280 ValueSize: 5,
1281 MaxEntries: 1,
1282 })
1283 if err != nil {
1284 t.Fatal(err)
1285 }
1286 defer arr.Close()
1287
1288 values := []*customEncoding{
1289 {"hello"},
1290 {"world"},
1291 }
1292 if err := arr.Put(uint32(0), values); err != nil {
1293 t.Fatal(err)
1294 }
1295
1296
1297 var retrieved []*customEncoding
1298 if err := arr.Lookup(uint32(0), &retrieved); err != nil {
1299 t.Fatal("Can't retrieve key 0:", err)
1300 }
1301
1302 for i, want := range []string{"HELLO", "WORLD"} {
1303 if retrieved[i] == nil {
1304 t.Error("First item is nil")
1305 } else if have := retrieved[i].data; have != want {
1306 t.Errorf("Put doesn't use BinaryMarshaler, expected %s but got %s", want, have)
1307 }
1308 }
1309
1310 })
1311 }
1312 }
1313
1314 type bpfCgroupStorageKey struct {
1315 CgroupInodeId uint64
1316 AttachType AttachType
1317 _ [4]byte
1318 }
1319
1320 func TestCgroupPerCPUStorageMarshaling(t *testing.T) {
1321 numCPU, err := internal.PossibleCPUs()
1322 if err != nil {
1323 t.Fatal(err)
1324 }
1325 if numCPU < 2 {
1326 t.Skip("Test requires at least two CPUs")
1327 }
1328 testutils.SkipOnOldKernel(t, "5.9", "per-CPU CGoup storage with write from user space support")
1329
1330 cgroup := testutils.CreateCgroup(t)
1331
1332 arr, err := NewMap(&MapSpec{
1333 Type: PerCPUCGroupStorage,
1334 KeySize: uint32(unsafe.Sizeof(bpfCgroupStorageKey{})),
1335 ValueSize: uint32(unsafe.Sizeof(uint64(0))),
1336 })
1337 if err != nil {
1338 t.Fatal(err)
1339 }
1340 t.Cleanup(func() {
1341 arr.Close()
1342 })
1343
1344 prog, err := NewProgram(&ProgramSpec{
1345 Type: CGroupSKB,
1346 AttachType: AttachCGroupInetEgress,
1347 License: "MIT",
1348 Instructions: asm.Instructions{
1349 asm.LoadMapPtr(asm.R1, arr.FD()),
1350 asm.Mov.Imm(asm.R2, 0),
1351 asm.FnGetLocalStorage.Call(),
1352 asm.Mov.Imm(asm.R0, 0),
1353 asm.Return(),
1354 },
1355 })
1356 if err != nil {
1357 t.Fatal(err)
1358 }
1359 defer prog.Close()
1360
1361 progAttachAttrs := sys.ProgAttachAttr{
1362 TargetFd: uint32(cgroup.Fd()),
1363 AttachBpfFd: uint32(prog.FD()),
1364 AttachType: uint32(AttachCGroupInetEgress),
1365 AttachFlags: 0,
1366 ReplaceBpfFd: 0,
1367 }
1368 err = sys.ProgAttach(&progAttachAttrs)
1369 if err != nil {
1370 t.Fatal(err)
1371 }
1372 defer func() {
1373 attr := sys.ProgDetachAttr{
1374 TargetFd: uint32(cgroup.Fd()),
1375 AttachBpfFd: uint32(prog.FD()),
1376 AttachType: uint32(AttachCGroupInetEgress),
1377 }
1378 if err := sys.ProgDetach(&attr); err != nil {
1379 t.Fatal(err)
1380 }
1381 }()
1382
1383 var mapKey = &bpfCgroupStorageKey{
1384 CgroupInodeId: testutils.GetCgroupIno(t, cgroup),
1385 AttachType: AttachCGroupInetEgress,
1386 }
1387
1388 values := []uint64{1, 2}
1389 if err := arr.Put(mapKey, values); err != nil {
1390 t.Fatalf("Can't set cgroup %s storage: %s", cgroup.Name(), err)
1391 }
1392
1393 var retrieved []uint64
1394 if err := arr.Lookup(mapKey, &retrieved); err != nil {
1395 t.Fatalf("Can't retrieve cgroup %s storage: %s", cgroup.Name(), err)
1396 }
1397
1398 for i, want := range []uint64{1, 2} {
1399 if retrieved[i] == 0 {
1400 t.Errorf("Item %d is 0", i)
1401 } else if have := retrieved[i]; have != want {
1402 t.Errorf("PerCPUCGroupStorage map is not correctly unmarshaled, expected %d but got %d", want, have)
1403 }
1404 }
1405 }
1406
1407 func TestMapMarshalUnsafe(t *testing.T) {
1408 m, err := NewMap(&MapSpec{
1409 Type: Hash,
1410 KeySize: 4,
1411 ValueSize: 4,
1412 MaxEntries: 1,
1413 })
1414 if err != nil {
1415 t.Fatal(err)
1416 }
1417 defer m.Close()
1418
1419 key := uint32(1)
1420 value := uint32(42)
1421
1422 if err := m.Put(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil {
1423 t.Fatal(err)
1424 }
1425
1426 var res uint32
1427 if err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&res)); err != nil {
1428 t.Fatal("Can't get item:", err)
1429 }
1430
1431 var sum uint32
1432 iter := m.Iterate()
1433 for iter.Next(&key, unsafe.Pointer(&res)) {
1434 sum += res
1435 }
1436 if err := iter.Err(); err != nil {
1437 t.Fatal(err)
1438 }
1439
1440 if res != 42 {
1441 t.Fatalf("Expected 42, got %d", res)
1442 }
1443
1444 iter = m.Iterate()
1445 iter.Next(unsafe.Pointer(&key), &res)
1446 if err := iter.Err(); err != nil {
1447 t.Error(err)
1448 }
1449 if key != 1 {
1450 t.Errorf("Expected key 1, got %d", key)
1451 }
1452
1453 if err := m.Delete(unsafe.Pointer(&key)); err != nil {
1454 t.Fatal("Can't delete:", err)
1455 }
1456 }
1457
1458 func TestMapName(t *testing.T) {
1459 if err := haveObjName(); err != nil {
1460 t.Skip(err)
1461 }
1462
1463 m, err := NewMap(&MapSpec{
1464 Name: "test",
1465 Type: Array,
1466 KeySize: 4,
1467 ValueSize: 4,
1468 MaxEntries: 1,
1469 })
1470 if err != nil {
1471 t.Fatal(err)
1472 }
1473 defer m.Close()
1474
1475 var info sys.MapInfo
1476 if err := sys.ObjInfo(m.fd, &info); err != nil {
1477 t.Fatal(err)
1478 }
1479
1480 if name := unix.ByteSliceToString(info.Name[:]); name != "test" {
1481 t.Error("Expected name to be test, got", name)
1482 }
1483 }
1484
1485 func TestMapFromFD(t *testing.T) {
1486 m := createArray(t)
1487 defer m.Close()
1488
1489 if err := m.Put(uint32(0), uint32(123)); err != nil {
1490 t.Fatal(err)
1491 }
1492
1493
1494
1495 m2, err := NewMapFromFD(m.FD())
1496 testutils.SkipIfNotSupported(t, err)
1497 if err != nil {
1498 t.Fatal(err)
1499 }
1500
1501
1502
1503
1504
1505
1506 m2.fd.Forget()
1507
1508 var val uint32
1509 if err := m2.Lookup(uint32(0), &val); err != nil {
1510 t.Fatal("Can't look up key:", err)
1511 }
1512
1513 if val != 123 {
1514 t.Error("Wrong value")
1515 }
1516 }
1517
1518 func TestMapContents(t *testing.T) {
1519 spec := &MapSpec{
1520 Type: Array,
1521 KeySize: 4,
1522 ValueSize: 4,
1523 MaxEntries: 2,
1524 Contents: []MapKV{
1525 {uint32(0), uint32(23)},
1526 {uint32(1), uint32(42)},
1527 },
1528 }
1529
1530 m, err := NewMap(spec)
1531 if err != nil {
1532 t.Fatal("Can't create map:", err)
1533 }
1534 defer m.Close()
1535
1536 var value uint32
1537 if err := m.Lookup(uint32(0), &value); err != nil {
1538 t.Error("Can't look up key 0:", err)
1539 } else if value != 23 {
1540 t.Errorf("Incorrect value for key 0, expected 23, have %d", value)
1541 }
1542
1543 if err := m.Lookup(uint32(1), &value); err != nil {
1544 t.Error("Can't look up key 1:", err)
1545 } else if value != 42 {
1546 t.Errorf("Incorrect value for key 0, expected 23, have %d", value)
1547 }
1548
1549 spec.Contents = []MapKV{
1550
1551 {uint32(14), uint32(0)},
1552 }
1553
1554 if _, err = NewMap(spec); err == nil {
1555 t.Error("Invalid contents should be rejected")
1556 }
1557 }
1558
1559 func TestMapFreeze(t *testing.T) {
1560 arr := createArray(t)
1561 defer arr.Close()
1562
1563 err := arr.Freeze()
1564 testutils.SkipIfNotSupported(t, err)
1565
1566 if err != nil {
1567 t.Fatal("Can't freeze map:", err)
1568 }
1569
1570 if err := arr.Put(uint32(0), uint32(1)); err == nil {
1571 t.Error("Freeze doesn't prevent modification from user space")
1572 }
1573 }
1574
1575 func TestMapGetNextID(t *testing.T) {
1576 testutils.SkipOnOldKernel(t, "4.13", "bpf_map_get_next_id")
1577 var next MapID
1578 var err error
1579
1580 hash := createHash()
1581 defer hash.Close()
1582
1583 if next, err = MapGetNextID(MapID(0)); err != nil {
1584 t.Fatal("Can't get next ID:", err)
1585 }
1586 if next == MapID(0) {
1587 t.Fatal("Expected next ID other than 0")
1588 }
1589
1590
1591
1592 for {
1593 last := next
1594 if next, err = MapGetNextID(last); err != nil {
1595 if !errors.Is(err, os.ErrNotExist) {
1596 t.Fatal("Expected ErrNotExist, got:", err)
1597 }
1598 break
1599 }
1600 if next <= last {
1601 t.Fatalf("Expected next ID (%d) to be higher than the last ID (%d)", next, last)
1602 }
1603 }
1604 }
1605
1606 func TestNewMapFromID(t *testing.T) {
1607 hash := createHash()
1608 defer hash.Close()
1609
1610 info, err := hash.Info()
1611 testutils.SkipIfNotSupported(t, err)
1612 if err != nil {
1613 t.Fatal("Couldn't get map info:", err)
1614 }
1615
1616 id, ok := info.ID()
1617 if !ok {
1618 t.Skip("Map ID not supported")
1619 }
1620
1621 hash2, err := NewMapFromID(id)
1622 if err != nil {
1623 t.Fatalf("Can't get map for ID %d: %v", id, err)
1624 }
1625 hash2.Close()
1626
1627
1628 _, err = NewMapFromID(MapID(math.MaxUint32))
1629 if !errors.Is(err, os.ErrNotExist) {
1630 t.Fatal("Expected ErrNotExist, got:", err)
1631 }
1632 }
1633
1634 func TestMapPinning(t *testing.T) {
1635 tmp := testutils.TempBPFFS(t)
1636 c := qt.New(t)
1637
1638 spec := &MapSpec{
1639 Name: "test",
1640 Type: Hash,
1641 KeySize: 4,
1642 ValueSize: 4,
1643 MaxEntries: 1,
1644 Pinning: PinByName,
1645 }
1646
1647 m1, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp})
1648 if err != nil {
1649 t.Fatal("Can't create map:", err)
1650 }
1651 defer m1.Close()
1652 pinned := m1.IsPinned()
1653 c.Assert(pinned, qt.IsTrue)
1654
1655 if err := m1.Put(uint32(0), uint32(42)); err != nil {
1656 t.Fatal("Can't write value:", err)
1657 }
1658
1659
1660
1661
1662 spec.BTF = new(btf.Spec)
1663
1664 m2, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp})
1665 testutils.SkipIfNotSupported(t, err)
1666 if err != nil {
1667 t.Fatal("Can't create map:", err)
1668 }
1669 defer m2.Close()
1670
1671 var value uint32
1672 if err := m2.Lookup(uint32(0), &value); err != nil {
1673 t.Fatal("Can't read from map:", err)
1674 }
1675
1676 if value != 42 {
1677 t.Fatal("Pinning doesn't use pinned maps")
1678 }
1679
1680 spec.KeySize = 8
1681 m3, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp})
1682 if err == nil {
1683 m3.Close()
1684 t.Fatalf("Opening a pinned map with a mismatching spec did not fail")
1685 }
1686 if !errors.Is(err, ErrMapIncompatible) {
1687 t.Fatalf("Opening a pinned map with a mismatching spec failed with the wrong error")
1688 }
1689 }
1690
1691 type benchValue struct {
1692 ID uint32
1693 Val16 uint16
1694 Val16_2 uint16
1695 Name [8]byte
1696 LID uint64
1697 }
1698
1699 type customBenchValue benchValue
1700
1701 func (cbv *customBenchValue) UnmarshalBinary(buf []byte) error {
1702 cbv.ID = internal.NativeEndian.Uint32(buf)
1703 cbv.Val16 = internal.NativeEndian.Uint16(buf[4:])
1704 cbv.Val16_2 = internal.NativeEndian.Uint16(buf[6:])
1705 copy(cbv.Name[:], buf[8:])
1706 cbv.LID = internal.NativeEndian.Uint64(buf[16:])
1707 return nil
1708 }
1709
1710 func (cbv *customBenchValue) MarshalBinary() ([]byte, error) {
1711 buf := make([]byte, 24)
1712 internal.NativeEndian.PutUint32(buf, cbv.ID)
1713 internal.NativeEndian.PutUint16(buf[4:], cbv.Val16)
1714 internal.NativeEndian.PutUint16(buf[6:], cbv.Val16_2)
1715 copy(buf[8:], cbv.Name[:])
1716 internal.NativeEndian.PutUint64(buf[16:], cbv.LID)
1717 return buf, nil
1718 }
1719
1720 func BenchmarkMarshalling(b *testing.B) {
1721 newMap := func(valueSize uint32) *Map {
1722 m, err := NewMap(&MapSpec{
1723 Type: Hash,
1724 KeySize: 8,
1725 ValueSize: valueSize,
1726 MaxEntries: 1,
1727 })
1728 if err != nil {
1729 b.Fatal(err)
1730 }
1731 return m
1732 }
1733
1734 key := uint64(0)
1735
1736 m := newMap(24)
1737 if err := m.Put(key, benchValue{}); err != nil {
1738 b.Fatal(err)
1739 }
1740
1741 b.Run("reflection", func(b *testing.B) {
1742 b.ReportAllocs()
1743 b.ResetTimer()
1744
1745 var value benchValue
1746
1747 for i := 0; i < b.N; i++ {
1748 err := m.Lookup(unsafe.Pointer(&key), &value)
1749 if err != nil {
1750 b.Fatal("Can't get key:", err)
1751 }
1752 }
1753 })
1754
1755 b.Run("custom", func(b *testing.B) {
1756 b.ReportAllocs()
1757 b.ResetTimer()
1758
1759 var value customBenchValue
1760
1761 for i := 0; i < b.N; i++ {
1762 err := m.Lookup(unsafe.Pointer(&key), &value)
1763 if err != nil {
1764 b.Fatal("Can't get key:", err)
1765 }
1766 }
1767 })
1768
1769 b.Run("unsafe", func(b *testing.B) {
1770 b.ReportAllocs()
1771 b.ResetTimer()
1772
1773 var value benchValue
1774
1775 for i := 0; i < b.N; i++ {
1776 err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value))
1777 if err != nil {
1778 b.Fatal("Can't get key:", err)
1779 }
1780 }
1781 })
1782 }
1783
1784 func BenchmarkPerCPUMarshalling(b *testing.B) {
1785 newMap := func(valueSize uint32) *Map {
1786 m, err := NewMap(&MapSpec{
1787 Type: PerCPUHash,
1788 KeySize: 8,
1789 ValueSize: valueSize,
1790 MaxEntries: 1,
1791 })
1792 if err != nil {
1793 b.Fatal(err)
1794 }
1795 return m
1796 }
1797
1798 key := uint64(1)
1799 val := []uint64{1, 2, 3, 4, 5, 6, 7, 8}
1800
1801 m := newMap(8)
1802 if err := m.Put(key, val[0:]); err != nil {
1803 b.Fatal(err)
1804 }
1805
1806 b.Run("reflection", func(b *testing.B) {
1807 b.ReportAllocs()
1808 b.ResetTimer()
1809
1810 var value []uint64
1811
1812 for i := 0; i < b.N; i++ {
1813 err := m.Lookup(unsafe.Pointer(&key), &value)
1814 if err != nil {
1815 b.Fatal("Can't get key:", err)
1816 }
1817 }
1818 })
1819 }
1820
1821 func BenchmarkMap(b *testing.B) {
1822 m, err := NewMap(&MapSpec{
1823 Type: Hash,
1824 KeySize: 4,
1825 ValueSize: 4,
1826 MaxEntries: 1,
1827 })
1828 if err != nil {
1829 b.Fatal(err)
1830 }
1831
1832 if err := m.Put(uint32(0), uint32(42)); err != nil {
1833 b.Fatal(err)
1834 }
1835
1836 b.Run("Lookup", func(b *testing.B) {
1837 var key, value uint32
1838
1839 b.ReportAllocs()
1840
1841 for i := 0; i < b.N; i++ {
1842 err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value))
1843 if err != nil {
1844 b.Fatal(err)
1845 }
1846 }
1847 })
1848
1849 b.Run("Update", func(b *testing.B) {
1850 var key, value uint32
1851
1852 b.ReportAllocs()
1853
1854 for i := 0; i < b.N; i++ {
1855 err := m.Update(unsafe.Pointer(&key), unsafe.Pointer(&value), UpdateAny)
1856 if err != nil {
1857 b.Fatal(err)
1858 }
1859 }
1860 })
1861
1862 b.Run("NextKey", func(b *testing.B) {
1863 var key uint32
1864
1865 b.ReportAllocs()
1866
1867 for i := 0; i < b.N; i++ {
1868 err := m.NextKey(nil, unsafe.Pointer(&key))
1869 if err != nil {
1870 b.Fatal(err)
1871 }
1872 }
1873 })
1874
1875 b.Run("Delete", func(b *testing.B) {
1876 var key uint32
1877
1878 b.ReportAllocs()
1879
1880 for i := 0; i < b.N; i++ {
1881 err := m.Delete(unsafe.Pointer(&key))
1882 if err != nil && !errors.Is(err, ErrKeyNotExist) {
1883 b.Fatal(err)
1884 }
1885 }
1886 })
1887 }
1888
1889
1890
1891 func ExampleMap_perCPU() {
1892 arr, err := NewMap(&MapSpec{
1893 Type: PerCPUArray,
1894 KeySize: 4,
1895 ValueSize: 4,
1896 MaxEntries: 2,
1897 })
1898 if err != nil {
1899 panic(err)
1900 }
1901
1902 first := []uint32{4, 5}
1903 if err := arr.Put(uint32(0), first); err != nil {
1904 panic(err)
1905 }
1906
1907 second := []uint32{2, 8}
1908 if err := arr.Put(uint32(1), second); err != nil {
1909 panic(err)
1910 }
1911
1912 var values []uint32
1913 if err := arr.Lookup(uint32(0), &values); err != nil {
1914 panic(err)
1915 }
1916 fmt.Println("First two values:", values[:2])
1917
1918 var (
1919 key uint32
1920 entries = arr.Iterate()
1921 )
1922
1923 for entries.Next(&key, &values) {
1924
1925 var sum uint32
1926 for _, n := range values {
1927 sum += n
1928 }
1929 fmt.Printf("Sum of %d: %d\n", key, sum)
1930 }
1931
1932 if err := entries.Err(); err != nil {
1933 panic(err)
1934 }
1935 }
1936
1937
1938
1939
1940
1941
1942
1943 func ExampleMap_zeroCopy() {
1944 hash := createHash()
1945 defer hash.Close()
1946
1947 key := [5]byte{'h', 'e', 'l', 'l', 'o'}
1948 value := uint32(23)
1949
1950 if err := hash.Put(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil {
1951 panic(err)
1952 }
1953
1954 value = 0
1955 if err := hash.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil {
1956 panic("can't get value:" + err.Error())
1957 }
1958
1959 fmt.Printf("The value is: %d\n", value)
1960
1961 }
1962
1963 func createHash() *Map {
1964 hash, err := NewMap(&MapSpec{
1965 Type: Hash,
1966 KeySize: 5,
1967 ValueSize: 4,
1968 MaxEntries: 10,
1969 })
1970 if err != nil {
1971 panic(err)
1972 }
1973 return hash
1974 }
1975
1976 func ExampleMap_NextKey() {
1977 hash := createHash()
1978 defer hash.Close()
1979
1980 if err := hash.Put("hello", uint32(21)); err != nil {
1981 panic(err)
1982 }
1983
1984 if err := hash.Put("world", uint32(42)); err != nil {
1985 panic(err)
1986 }
1987
1988 var firstKey string
1989 if err := hash.NextKey(nil, &firstKey); err != nil {
1990 panic(err)
1991 }
1992
1993 var nextKey string
1994 if err := hash.NextKey(firstKey, &nextKey); err != nil {
1995 panic(err)
1996 }
1997
1998
1999 }
2000
2001
2002
2003 func ExampleMap_Iterate() {
2004 hash := createHash()
2005 defer hash.Close()
2006
2007 if err := hash.Put("hello", uint32(21)); err != nil {
2008 panic(err)
2009 }
2010
2011 if err := hash.Put("world", uint32(42)); err != nil {
2012 panic(err)
2013 }
2014
2015 var (
2016 key string
2017 value uint32
2018 entries = hash.Iterate()
2019 )
2020
2021 for entries.Next(&key, &value) {
2022
2023 fmt.Printf("key: %s, value: %d\n", key, value)
2024 }
2025
2026 if err := entries.Err(); err != nil {
2027 panic(fmt.Sprint("Iterator encountered an error:", err))
2028 }
2029 }
2030
2031
2032
2033 func ExampleMap_Iterate_nestedMapsAndProgramArrays() {
2034 var arrayOfMaps *Map
2035
2036 var (
2037 key uint32
2038 m *Map
2039 entries = arrayOfMaps.Iterate()
2040 )
2041
2042
2043
2044 defer m.Close()
2045
2046 for entries.Next(&key, &m) {
2047
2048 fmt.Printf("key: %v, map: %v\n", key, m)
2049 }
2050
2051 if err := entries.Err(); err != nil {
2052 panic(fmt.Sprint("Iterator encountered an error:", err))
2053 }
2054 }
2055
View as plain text