...

Source file src/github.com/cilium/ebpf/prog_test.go

Documentation: github.com/cilium/ebpf

     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/cilium/ebpf/asm"
    20  	"github.com/cilium/ebpf/btf"
    21  	"github.com/cilium/ebpf/internal"
    22  	"github.com/cilium/ebpf/internal/sys"
    23  	"github.com/cilium/ebpf/internal/testutils"
    24  	"github.com/cilium/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  }
   926  

View as plain text