github.com/kubeshark/ebpf@v0.9.2/map_test.go (about) 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/kubeshark/ebpf/asm" 15 "github.com/kubeshark/ebpf/btf" 16 "github.com/kubeshark/ebpf/internal" 17 "github.com/kubeshark/ebpf/internal/sys" 18 "github.com/kubeshark/ebpf/internal/testutils" 19 "github.com/kubeshark/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 // Introduced in commit 6e71b04a8224. 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 // Make sure we can still access the original map 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 // Do not copy this, use Clone instead. 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 // Some empty arrays like sockmap don't return any keys. 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 // Make sure unmarshaling works on slices containing pointers 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 // Padding 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 // If you're thinking about copying this, don't. Use 1494 // Clone() instead. 1495 m2, err := NewMapFromFD(m.FD()) 1496 testutils.SkipIfNotSupported(t, err) 1497 if err != nil { 1498 t.Fatal(err) 1499 } 1500 1501 // Both m and m2 refer to the same fd now. Closing either of them will 1502 // release the fd to the OS, which then might re-use that fd for another 1503 // test. Once we close the second map we might close the re-used fd 1504 // inadvertently, leading to spurious test failures. 1505 // To avoid this we have to "leak" one of the maps. 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 // Key is larger than MaxEntries 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 // As there can be multiple eBPF maps, we loop over all of them and 1591 // make sure, the IDs increase and the last call will return ErrNotExist 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 // As there can be multiple maps, we use max(uint32) as MapID to trigger an expected error. 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 // This is a terrible hack: if loading a pinned map tries to load BTF, 1660 // it will get a nil *btf.Spec from this *btf.Map. This is turn will make 1661 // btf.NewHandle fail. 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 // Per CPU maps store a distinct value for each CPU. They are useful 1890 // to collect metrics. 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 // NB: sum can overflow, real code should check for this 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 // It is possible to use unsafe.Pointer to avoid marshalling 1938 // and copy overhead. It is the resposibility of the caller to ensure 1939 // the correct size of unsafe.Pointers. 1940 // 1941 // Note that using unsafe.Pointer is only marginally faster than 1942 // implementing Marshaler on the type. 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 // Output: The value is: 23 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 // Order of keys is non-deterministic due to randomized map seed 1999 } 2000 2001 // ExampleMap_Iterate demonstrates how to iterate over all entries 2002 // in a map. 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 // Order of keys is non-deterministic due to randomized map seed 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 // It is possible to iterate nested maps and program arrays by 2032 // unmarshaling into a *Map or *Program. 2033 func ExampleMap_Iterate_nestedMapsAndProgramArrays() { 2034 var arrayOfMaps *Map // Set this up somehow 2035 2036 var ( 2037 key uint32 2038 m *Map 2039 entries = arrayOfMaps.Iterate() 2040 ) 2041 2042 // Make sure that the iterated map is closed after 2043 // we are done. 2044 defer m.Close() 2045 2046 for entries.Next(&key, &m) { 2047 // Order of keys is non-deterministic due to randomized map seed 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 }