github.com/kubeshark/ebpf@v0.9.2/prog_test.go (about) 1 package ebpf 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "math" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "syscall" 14 "testing" 15 "time" 16 17 qt "github.com/frankban/quicktest" 18 19 "github.com/kubeshark/ebpf/asm" 20 "github.com/kubeshark/ebpf/btf" 21 "github.com/kubeshark/ebpf/internal" 22 "github.com/kubeshark/ebpf/internal/sys" 23 "github.com/kubeshark/ebpf/internal/testutils" 24 "github.com/kubeshark/ebpf/internal/unix" 25 ) 26 27 func TestProgramRun(t *testing.T) { 28 testutils.SkipOnOldKernel(t, "4.8", "XDP program") 29 30 pat := []byte{0xDE, 0xAD, 0xBE, 0xEF} 31 buf := make([]byte, 14) 32 33 // r1 : ctx_start 34 // r1+4: ctx_end 35 ins := asm.Instructions{ 36 // r2 = *(r1+4) 37 asm.LoadMem(asm.R2, asm.R1, 4, asm.Word), 38 // r1 = *(r1+0) 39 asm.LoadMem(asm.R1, asm.R1, 0, asm.Word), 40 // r3 = r1 41 asm.Mov.Reg(asm.R3, asm.R1), 42 // r3 += len(buf) 43 asm.Add.Imm(asm.R3, int32(len(buf))), 44 // if r3 > r2 goto +len(pat) 45 asm.JGT.Reg(asm.R3, asm.R2, "out"), 46 } 47 for i, b := range pat { 48 ins = append(ins, asm.StoreImm(asm.R1, int16(i), int64(b), asm.Byte)) 49 } 50 ins = append(ins, 51 // return 42 52 asm.LoadImm(asm.R0, 42, asm.DWord).WithSymbol("out"), 53 asm.Return(), 54 ) 55 56 t.Log(ins) 57 58 prog, err := NewProgram(&ProgramSpec{ 59 Name: "test", 60 Type: XDP, 61 Instructions: ins, 62 License: "MIT", 63 }) 64 if err != nil { 65 t.Fatal(err) 66 } 67 defer prog.Close() 68 69 p2, err := prog.Clone() 70 if err != nil { 71 t.Fatal("Can't clone program") 72 } 73 defer p2.Close() 74 75 prog.Close() 76 prog = p2 77 78 ret, out, err := prog.Test(buf) 79 testutils.SkipIfNotSupported(t, err) 80 if err != nil { 81 t.Fatal(err) 82 } 83 84 if ret != 42 { 85 t.Error("Expected return value to be 42, got", ret) 86 } 87 88 if !bytes.Equal(out[:len(pat)], pat) { 89 t.Errorf("Expected %v, got %v", pat, out) 90 } 91 } 92 93 func TestProgramRunWithOptions(t *testing.T) { 94 testutils.SkipOnOldKernel(t, "5.15", "XDP ctx_in/ctx_out") 95 96 ins := asm.Instructions{ 97 // Return XDP_ABORTED 98 asm.LoadImm(asm.R0, 0, asm.DWord), 99 asm.Return(), 100 } 101 102 prog, err := NewProgram(&ProgramSpec{ 103 Name: "test", 104 Type: XDP, 105 Instructions: ins, 106 License: "MIT", 107 }) 108 if err != nil { 109 t.Fatal(err) 110 } 111 defer prog.Close() 112 113 buf := make([]byte, 14) 114 xdp := sys.XdpMd{ 115 Data: 0, 116 DataEnd: 14, 117 } 118 xdpOut := sys.XdpMd{} 119 opts := RunOptions{ 120 Data: buf, 121 Context: xdp, 122 ContextOut: &xdpOut, 123 } 124 ret, err := prog.Run(&opts) 125 testutils.SkipIfNotSupported(t, err) 126 if err != nil { 127 t.Fatal(err) 128 } 129 130 if ret != 0 { 131 t.Error("Expected return value to be 0, got", ret) 132 } 133 134 if xdp != xdpOut { 135 t.Errorf("Expect xdp (%+v) == xdpOut (%+v)", xdp, xdpOut) 136 } 137 } 138 139 func TestProgramRunEmptyData(t *testing.T) { 140 testutils.SkipOnOldKernel(t, "5.13", "sk_lookup BPF_PROG_RUN") 141 142 ins := asm.Instructions{ 143 // Return SK_DROP 144 asm.LoadImm(asm.R0, 0, asm.DWord), 145 asm.Return(), 146 } 147 148 prog, err := NewProgram(&ProgramSpec{ 149 Name: "test", 150 Type: SkLookup, 151 AttachType: AttachSkLookup, 152 Instructions: ins, 153 License: "MIT", 154 }) 155 if err != nil { 156 t.Fatal(err) 157 } 158 defer prog.Close() 159 160 opts := RunOptions{ 161 Context: sys.SkLookup{ 162 Family: syscall.AF_INET, 163 }, 164 } 165 ret, err := prog.Run(&opts) 166 testutils.SkipIfNotSupported(t, err) 167 if err != nil { 168 t.Fatal(err) 169 } 170 171 if ret != 0 { 172 t.Error("Expected return value to be 0, got", ret) 173 } 174 } 175 176 func TestProgramBenchmark(t *testing.T) { 177 prog := mustSocketFilter(t) 178 179 ret, duration, err := prog.Benchmark(make([]byte, 14), 1, nil) 180 testutils.SkipIfNotSupported(t, err) 181 if err != nil { 182 t.Fatal("Error from Benchmark:", err) 183 } 184 185 if ret != 2 { 186 t.Error("Expected return value 2, got", ret) 187 } 188 189 if duration == 0 { 190 t.Error("Expected non-zero duration") 191 } 192 } 193 194 func TestProgramTestRunInterrupt(t *testing.T) { 195 testutils.SkipOnOldKernel(t, "5.0", "EINTR from BPF_PROG_TEST_RUN") 196 197 prog := mustSocketFilter(t) 198 199 var ( 200 tgid = unix.Getpid() 201 tidChan = make(chan int, 1) 202 exit = make(chan struct{}) 203 errs = make(chan error, 1) 204 timeout = time.After(5 * time.Second) 205 ) 206 207 defer close(exit) 208 209 go func() { 210 runtime.LockOSThread() 211 defer func() { 212 // Wait for the test to allow us to unlock the OS thread, to 213 // ensure that we don't send SIGUSR1 to the wrong thread. 214 <-exit 215 runtime.UnlockOSThread() 216 }() 217 218 tidChan <- unix.Gettid() 219 220 // Block this thread in the BPF syscall, so that we can 221 // trigger EINTR by sending a signal. 222 opts := RunOptions{ 223 Data: make([]byte, 14), 224 Repeat: math.MaxInt32, 225 Reset: func() { 226 // We don't know how long finishing the 227 // test run would take, so flag that we've seen 228 // an interruption and abort the goroutine. 229 close(errs) 230 runtime.Goexit() 231 }, 232 } 233 _, _, err := prog.testRun(&opts) 234 235 errs <- err 236 }() 237 238 tid := <-tidChan 239 for { 240 err := unix.Tgkill(tgid, tid, syscall.SIGUSR1) 241 if err != nil { 242 t.Fatal("Can't send signal to goroutine thread:", err) 243 } 244 245 select { 246 case err, ok := <-errs: 247 if !ok { 248 return 249 } 250 251 testutils.SkipIfNotSupported(t, err) 252 if err == nil { 253 t.Fatal("testRun wasn't interrupted") 254 } 255 256 t.Fatal("testRun returned an error:", err) 257 258 case <-timeout: 259 t.Fatal("Timed out trying to interrupt the goroutine") 260 261 default: 262 } 263 } 264 } 265 266 func TestProgramClose(t *testing.T) { 267 prog := mustSocketFilter(t) 268 269 if err := prog.Close(); err != nil { 270 t.Fatal("Can't close program:", err) 271 } 272 } 273 274 func TestProgramPin(t *testing.T) { 275 prog := mustSocketFilter(t) 276 c := qt.New(t) 277 278 tmp := testutils.TempBPFFS(t) 279 280 path := filepath.Join(tmp, "program") 281 if err := prog.Pin(path); err != nil { 282 t.Fatal(err) 283 } 284 285 pinned := prog.IsPinned() 286 c.Assert(pinned, qt.IsTrue) 287 288 prog.Close() 289 290 prog, err := LoadPinnedProgram(path, nil) 291 testutils.SkipIfNotSupported(t, err) 292 if err != nil { 293 t.Fatal(err) 294 } 295 defer prog.Close() 296 297 if prog.Type() != SocketFilter { 298 t.Error("Expected pinned program to have type SocketFilter, but got", prog.Type()) 299 } 300 301 if !prog.IsPinned() { 302 t.Error("Expected IsPinned to be true") 303 } 304 } 305 306 func TestProgramUnpin(t *testing.T) { 307 prog := mustSocketFilter(t) 308 c := qt.New(t) 309 310 tmp := testutils.TempBPFFS(t) 311 312 path := filepath.Join(tmp, "program") 313 if err := prog.Pin(path); err != nil { 314 t.Fatal(err) 315 } 316 317 pinned := prog.IsPinned() 318 c.Assert(pinned, qt.IsTrue) 319 320 if err := prog.Unpin(); err != nil { 321 t.Fatal("Failed to unpin program:", err) 322 } 323 if _, err := os.Stat(path); err == nil { 324 t.Fatal("Pinned program path still exists after unpinning:", err) 325 } 326 } 327 328 func TestProgramLoadPinnedWithFlags(t *testing.T) { 329 // Introduced in commit 6e71b04a8224. 330 testutils.SkipOnOldKernel(t, "4.14", "file_flags in BPF_OBJ_GET") 331 332 prog := mustSocketFilter(t) 333 334 tmp := testutils.TempBPFFS(t) 335 336 path := filepath.Join(tmp, "program") 337 if err := prog.Pin(path); err != nil { 338 t.Fatal(err) 339 } 340 341 prog.Close() 342 343 _, err := LoadPinnedProgram(path, &LoadPinOptions{ 344 Flags: math.MaxUint32, 345 }) 346 testutils.SkipIfNotSupported(t, err) 347 if !errors.Is(err, unix.EINVAL) { 348 t.Fatal("Invalid flags don't trigger an error:", err) 349 } 350 } 351 352 func TestProgramVerifierOutputOnError(t *testing.T) { 353 _, err := NewProgram(&ProgramSpec{ 354 Type: SocketFilter, 355 Instructions: asm.Instructions{ 356 asm.Return(), 357 }, 358 License: "MIT", 359 }) 360 if err == nil { 361 t.Fatal("Expected program to be invalid") 362 } 363 364 var ve *VerifierError 365 if !errors.As(err, &ve) { 366 t.Fatal("Error does not contain a VerifierError") 367 } 368 369 if !strings.Contains(ve.Error(), "R0 !read_ok") { 370 t.Error("Unexpected verifier error contents:", ve) 371 } 372 } 373 374 func TestProgramKernelVersion(t *testing.T) { 375 testutils.SkipOnOldKernel(t, "4.20", "KernelVersion") 376 prog, err := NewProgram(&ProgramSpec{ 377 Type: Kprobe, 378 Instructions: asm.Instructions{ 379 asm.LoadImm(asm.R0, 0, asm.DWord), 380 asm.Return(), 381 }, 382 KernelVersion: 42, 383 License: "MIT", 384 }) 385 if err != nil { 386 t.Fatal("Could not load Kprobe program") 387 } 388 defer prog.Close() 389 } 390 391 func TestProgramVerifierOutput(t *testing.T) { 392 prog, err := NewProgramWithOptions(socketFilterSpec, ProgramOptions{ 393 LogLevel: 2, 394 }) 395 if err != nil { 396 t.Fatal(err) 397 } 398 defer prog.Close() 399 400 if prog.VerifierLog == "" { 401 t.Error("Expected VerifierLog to be present") 402 } 403 404 // Issue 64 405 _, err = NewProgramWithOptions(&ProgramSpec{ 406 Type: SocketFilter, 407 Instructions: asm.Instructions{ 408 asm.Mov.Reg(asm.R0, asm.R1), 409 }, 410 License: "MIT", 411 }, ProgramOptions{ 412 LogLevel: 2, 413 }) 414 415 if err == nil { 416 t.Fatal("Expected an error from invalid program") 417 } 418 419 var ve *internal.VerifierError 420 if !errors.As(err, &ve) { 421 t.Error("Error is not a VerifierError") 422 } 423 } 424 425 func TestProgramWithUnsatisfiedMap(t *testing.T) { 426 coll, err := LoadCollectionSpec("testdata/loader-el.elf") 427 if err != nil { 428 t.Fatal(err) 429 } 430 431 // The program will have at least one map reference. 432 progSpec := coll.Programs["xdp_prog"] 433 progSpec.ByteOrder = nil 434 435 _, err = NewProgram(progSpec) 436 testutils.SkipIfNotSupported(t, err) 437 if !errors.Is(err, asm.ErrUnsatisfiedMapReference) { 438 t.Fatal("Expected an error wrapping asm.ErrUnsatisfiedMapReference, got", err) 439 } 440 t.Log(err) 441 } 442 443 func TestProgramName(t *testing.T) { 444 if err := haveObjName(); err != nil { 445 t.Skip(err) 446 } 447 448 prog := mustSocketFilter(t) 449 450 var info sys.ProgInfo 451 if err := sys.ObjInfo(prog.fd, &info); err != nil { 452 t.Fatal(err) 453 } 454 455 if name := unix.ByteSliceToString(info.Name[:]); name != "test" { 456 t.Errorf("Name is not test, got '%s'", name) 457 } 458 } 459 460 func TestSanitizeName(t *testing.T) { 461 for input, want := range map[string]string{ 462 "test": "test", 463 "t-est": "test", 464 "t_est": "t_est", 465 "hörnchen": "hrnchen", 466 } { 467 if have := SanitizeName(input, -1); have != want { 468 t.Errorf("Wanted '%s' got '%s'", want, have) 469 } 470 } 471 } 472 473 func TestProgramCloneNil(t *testing.T) { 474 p, err := (*Program)(nil).Clone() 475 if err != nil { 476 t.Fatal(err) 477 } 478 479 if p != nil { 480 t.Fatal("Cloning a nil Program doesn't return nil") 481 } 482 } 483 484 func TestProgramMarshaling(t *testing.T) { 485 const idx = uint32(0) 486 487 arr := createProgramArray(t) 488 defer arr.Close() 489 490 prog := mustSocketFilter(t) 491 492 if err := arr.Put(idx, prog); err != nil { 493 t.Fatal("Can't put program:", err) 494 } 495 496 if err := arr.Lookup(idx, Program{}); err == nil { 497 t.Fatal("Lookup accepts non-pointer Program") 498 } 499 500 var prog2 *Program 501 defer prog2.Close() 502 503 if err := arr.Lookup(idx, prog2); err == nil { 504 t.Fatal("Get accepts *Program") 505 } 506 507 testutils.SkipOnOldKernel(t, "4.12", "lookup for ProgramArray") 508 509 if err := arr.Lookup(idx, &prog2); err != nil { 510 t.Fatal("Can't unmarshal program:", err) 511 } 512 defer prog2.Close() 513 514 if prog2 == nil { 515 t.Fatal("Unmarshalling set program to nil") 516 } 517 } 518 519 func TestProgramFromFD(t *testing.T) { 520 prog := mustSocketFilter(t) 521 522 // If you're thinking about copying this, don't. Use 523 // Clone() instead. 524 prog2, err := NewProgramFromFD(prog.FD()) 525 testutils.SkipIfNotSupported(t, err) 526 if err != nil { 527 t.Fatal(err) 528 } 529 530 // Both programs refer to the same fd now. Closing either of them will 531 // release the fd to the OS, which then might re-use that fd for another 532 // test. Once we close the second map we might close the re-used fd 533 // inadvertently, leading to spurious test failures. 534 // To avoid this we have to "leak" one of the programs. 535 prog2.fd.Forget() 536 } 537 538 func TestHaveProgTestRun(t *testing.T) { 539 testutils.CheckFeatureTest(t, haveProgTestRun) 540 } 541 542 func TestProgramGetNextID(t *testing.T) { 543 testutils.SkipOnOldKernel(t, "4.13", "bpf_prog_get_next_id") 544 545 // Ensure there is at least one program loaded 546 _ = mustSocketFilter(t) 547 548 // As there can be multiple eBPF programs, we loop over all of them and 549 // make sure, the IDs increase and the last call will return ErrNotExist 550 last := ProgramID(0) 551 for { 552 next, err := ProgramGetNextID(last) 553 if errors.Is(err, os.ErrNotExist) { 554 if last == 0 { 555 t.Fatal("Got ErrNotExist on the first iteration") 556 } 557 break 558 } 559 if err != nil { 560 t.Fatal("Unexpected error:", err) 561 } 562 if next <= last { 563 t.Fatalf("Expected next ID (%d) to be higher than the last ID (%d)", next, last) 564 } 565 last = next 566 } 567 } 568 569 func TestNewProgramFromID(t *testing.T) { 570 prog := mustSocketFilter(t) 571 572 info, err := prog.Info() 573 testutils.SkipIfNotSupported(t, err) 574 if err != nil { 575 t.Fatal("Could not get program info:", err) 576 } 577 578 id, ok := info.ID() 579 if !ok { 580 t.Skip("Program ID not supported") 581 } 582 583 prog2, err := NewProgramFromID(id) 584 if err != nil { 585 t.Fatalf("Can't get FD for program ID %d: %v", id, err) 586 } 587 prog2.Close() 588 589 // As there can be multiple programs, we use max(uint32) as ProgramID to trigger an expected error. 590 _, err = NewProgramFromID(ProgramID(math.MaxUint32)) 591 if !errors.Is(err, os.ErrNotExist) { 592 t.Fatal("Expected ErrNotExist, got:", err) 593 } 594 } 595 596 func TestProgramRejectIncorrectByteOrder(t *testing.T) { 597 spec := socketFilterSpec.Copy() 598 599 spec.ByteOrder = binary.BigEndian 600 if internal.NativeEndian == binary.BigEndian { 601 spec.ByteOrder = binary.LittleEndian 602 } 603 604 _, err := NewProgram(spec) 605 if err == nil { 606 t.Error("Incorrect ByteOrder should be rejected at load time") 607 } 608 } 609 610 func TestProgramSpecTag(t *testing.T) { 611 arr := createArray(t) 612 defer arr.Close() 613 614 spec := &ProgramSpec{ 615 Type: SocketFilter, 616 Instructions: asm.Instructions{ 617 asm.LoadImm(asm.R0, -1, asm.DWord), 618 asm.LoadMapPtr(asm.R1, arr.FD()), 619 asm.Mov.Imm32(asm.R0, 0), 620 asm.Return(), 621 }, 622 License: "MIT", 623 } 624 625 prog, err := NewProgram(spec) 626 if err != nil { 627 t.Fatal(err) 628 } 629 defer prog.Close() 630 631 info, err := prog.Info() 632 testutils.SkipIfNotSupported(t, err) 633 if err != nil { 634 t.Fatal(err) 635 } 636 637 tag, err := spec.Tag() 638 if err != nil { 639 t.Fatal("Can't calculate tag:", err) 640 } 641 642 if tag != info.Tag { 643 t.Errorf("Calculated tag %s doesn't match kernel tag %s", tag, info.Tag) 644 } 645 } 646 647 func TestProgramTypeLSM(t *testing.T) { 648 lsmTests := []struct { 649 attachFn string 650 flags uint32 651 expectedErr bool 652 }{ 653 { 654 attachFn: "task_getpgid", 655 }, 656 { 657 attachFn: "task_setnice", 658 flags: unix.BPF_F_SLEEPABLE, 659 expectedErr: true, 660 }, 661 { 662 attachFn: "file_open", 663 flags: unix.BPF_F_SLEEPABLE, 664 }, 665 } 666 for _, tt := range lsmTests { 667 t.Run(tt.attachFn, func(t *testing.T) { 668 prog, err := NewProgram(&ProgramSpec{ 669 AttachTo: tt.attachFn, 670 AttachType: AttachLSMMac, 671 Instructions: asm.Instructions{ 672 asm.LoadImm(asm.R0, 0, asm.DWord), 673 asm.Return(), 674 }, 675 License: "GPL", 676 Type: LSM, 677 Flags: tt.flags, 678 }) 679 testutils.SkipIfNotSupported(t, err) 680 681 if tt.flags&unix.BPF_F_SLEEPABLE != 0 { 682 testutils.SkipOnOldKernel(t, "5.11", "BPF_F_SLEEPABLE for LSM progs") 683 } 684 if tt.expectedErr && err == nil { 685 t.Errorf("Test case '%s': expected error", tt.attachFn) 686 } 687 if !tt.expectedErr && err != nil { 688 t.Errorf("Test case '%s': expected success", tt.attachFn) 689 } 690 prog.Close() 691 }) 692 } 693 } 694 695 func TestProgramKernelTypes(t *testing.T) { 696 if _, err := os.Stat("/sys/kernel/btf/vmlinux"); os.IsNotExist(err) { 697 t.Skip("/sys/kernel/btf/vmlinux not present") 698 } 699 700 btfSpec, err := btf.LoadSpec("/sys/kernel/btf/vmlinux") 701 if err != nil { 702 t.Fatal(err) 703 } 704 705 prog, err := NewProgramWithOptions(&ProgramSpec{ 706 Type: Tracing, 707 AttachType: AttachTraceIter, 708 AttachTo: "bpf_map", 709 Instructions: asm.Instructions{ 710 asm.Mov.Imm(asm.R0, 0), 711 asm.Return(), 712 }, 713 License: "MIT", 714 }, ProgramOptions{ 715 KernelTypes: btfSpec, 716 }) 717 testutils.SkipIfNotSupported(t, err) 718 if err != nil { 719 t.Fatal("NewProgram with Target:", err) 720 } 721 prog.Close() 722 } 723 724 func TestProgramBindMap(t *testing.T) { 725 testutils.SkipOnOldKernel(t, "5.10", "BPF_PROG_BIND_MAP") 726 727 arr, err := NewMap(&MapSpec{ 728 Type: Array, 729 KeySize: 4, 730 ValueSize: 4, 731 MaxEntries: 1, 732 }) 733 if err != nil { 734 t.Errorf("Failed to load map: %v", err) 735 } 736 defer arr.Close() 737 738 prog := mustSocketFilter(t) 739 740 // The attached map does not contain BTF information. So 741 // the metadata part of the program will be empty. This 742 // test just makes sure that we can bind a map to a program. 743 if err := prog.BindMap(arr); err != nil { 744 t.Errorf("Failed to bind map to program: %v", err) 745 } 746 } 747 748 func TestProgramInstructions(t *testing.T) { 749 name := "test_prog" 750 spec := &ProgramSpec{ 751 Type: SocketFilter, 752 Name: name, 753 Instructions: asm.Instructions{ 754 asm.LoadImm(asm.R0, -1, asm.DWord).WithSymbol(name), 755 asm.Return(), 756 }, 757 License: "MIT", 758 } 759 760 prog, err := NewProgram(spec) 761 if err != nil { 762 t.Fatal(err) 763 } 764 defer prog.Close() 765 766 pi, err := prog.Info() 767 testutils.SkipIfNotSupported(t, err) 768 if err != nil { 769 t.Fatal(err) 770 } 771 772 insns, err := pi.Instructions() 773 if err != nil { 774 t.Fatal(err) 775 } 776 777 tag, err := spec.Tag() 778 if err != nil { 779 t.Fatal(err) 780 } 781 782 tagXlated, err := insns.Tag(internal.NativeEndian) 783 if err != nil { 784 t.Fatal(err) 785 } 786 787 if tag != tagXlated { 788 t.Fatalf("tag %s differs from xlated instructions tag %s", tag, tagXlated) 789 } 790 } 791 792 func createProgramArray(t *testing.T) *Map { 793 t.Helper() 794 795 arr, err := NewMap(&MapSpec{ 796 Type: ProgramArray, 797 KeySize: 4, 798 ValueSize: 4, 799 MaxEntries: 1, 800 }) 801 if err != nil { 802 t.Fatal(err) 803 } 804 return arr 805 } 806 807 var socketFilterSpec = &ProgramSpec{ 808 Name: "test", 809 Type: SocketFilter, 810 Instructions: asm.Instructions{ 811 asm.LoadImm(asm.R0, 2, asm.DWord), 812 asm.Return(), 813 }, 814 License: "MIT", 815 } 816 817 func mustSocketFilter(tb testing.TB) *Program { 818 tb.Helper() 819 820 prog, err := NewProgram(socketFilterSpec) 821 if err != nil { 822 tb.Fatal(err) 823 } 824 tb.Cleanup(func() { prog.Close() }) 825 826 return prog 827 } 828 829 // Retrieve a verifier error when loading a program fails. 830 func ExampleProgram_verifierError() { 831 _, err := NewProgram(&ProgramSpec{ 832 Type: SocketFilter, 833 Instructions: asm.Instructions{ 834 asm.LoadImm(asm.R0, 0, asm.DWord), 835 // Missing Return 836 }, 837 License: "MIT", 838 }) 839 840 var ve *VerifierError 841 if errors.As(err, &ve) { 842 // Using %+v will print the whole verifier error, not just the last 843 // few lines. 844 fmt.Printf("Verifier error: %+v\n", ve) 845 } 846 } 847 848 // Use NewProgramWithOptions if you'd like to get the verifier output 849 // for a program, or if you want to change the buffer size used when 850 // generating error messages. 851 func ExampleProgram_retrieveVerifierOutput() { 852 spec := &ProgramSpec{ 853 Type: SocketFilter, 854 Instructions: asm.Instructions{ 855 asm.LoadImm(asm.R0, 0, asm.DWord), 856 asm.Return(), 857 }, 858 License: "MIT", 859 } 860 861 prog, err := NewProgramWithOptions(spec, ProgramOptions{ 862 LogLevel: 2, 863 LogSize: 1024, 864 }) 865 if err != nil { 866 panic(err) 867 } 868 defer prog.Close() 869 870 fmt.Println("The verifier output is:") 871 fmt.Println(prog.VerifierLog) 872 } 873 874 // It's possible to read a program directly from a ProgramArray. 875 func ExampleProgram_unmarshalFromMap() { 876 progArray, err := LoadPinnedMap("/path/to/map", nil) 877 if err != nil { 878 panic(err) 879 } 880 defer progArray.Close() 881 882 // Load a single program 883 var prog *Program 884 if err := progArray.Lookup(uint32(0), &prog); err != nil { 885 panic(err) 886 } 887 defer prog.Close() 888 889 fmt.Println("first prog:", prog) 890 891 // Iterate all programs 892 var ( 893 key uint32 894 entries = progArray.Iterate() 895 ) 896 897 for entries.Next(&key, &prog) { 898 fmt.Println(key, "is", prog) 899 } 900 901 if err := entries.Err(); err != nil { 902 panic(err) 903 } 904 } 905 906 func ExampleProgramSpec_Tag() { 907 spec := &ProgramSpec{ 908 Type: SocketFilter, 909 Instructions: asm.Instructions{ 910 asm.LoadImm(asm.R0, 0, asm.DWord), 911 asm.Return(), 912 }, 913 License: "MIT", 914 } 915 916 prog, _ := NewProgram(spec) 917 info, _ := prog.Info() 918 tag, _ := spec.Tag() 919 920 if info.Tag != tag { 921 fmt.Printf("The tags don't match: %s != %s\n", info.Tag, tag) 922 } else { 923 fmt.Println("The programs are identical, tag is", tag) 924 } 925 }