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