...

Source file src/github.com/cilium/ebpf/link/link_test.go

Documentation: github.com/cilium/ebpf/link

     1  package link
     2  
     3  import (
     4  	"errors"
     5  	"math"
     6  	"os"
     7  	"path/filepath"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/cilium/ebpf"
    12  	"github.com/cilium/ebpf/asm"
    13  	"github.com/cilium/ebpf/internal/sys"
    14  	"github.com/cilium/ebpf/internal/testutils"
    15  	"github.com/cilium/ebpf/internal/unix"
    16  )
    17  
    18  func TestRawLink(t *testing.T) {
    19  	cgroup, prog := mustCgroupFixtures(t)
    20  
    21  	link, err := AttachRawLink(RawLinkOptions{
    22  		Target:  int(cgroup.Fd()),
    23  		Program: prog,
    24  		Attach:  ebpf.AttachCGroupInetEgress,
    25  	})
    26  	testutils.SkipIfNotSupported(t, err)
    27  	if err != nil {
    28  		t.Fatal("Can't create raw link:", err)
    29  	}
    30  
    31  	info, err := link.Info()
    32  	if err != nil {
    33  		t.Fatal("Can't get link info:", err)
    34  	}
    35  
    36  	pi, err := prog.Info()
    37  	if err != nil {
    38  		t.Fatal("Can't get program info:", err)
    39  	}
    40  
    41  	progID, ok := pi.ID()
    42  	if !ok {
    43  		t.Fatal("Program ID not available in program info")
    44  	}
    45  
    46  	if info.Program != progID {
    47  		t.Error("Link program ID doesn't match program ID")
    48  	}
    49  
    50  	testLink(t, &linkCgroup{*link}, prog)
    51  }
    52  
    53  func TestRawLinkLoadPinnedWithOptions(t *testing.T) {
    54  	cgroup, prog := mustCgroupFixtures(t)
    55  
    56  	link, err := AttachRawLink(RawLinkOptions{
    57  		Target:  int(cgroup.Fd()),
    58  		Program: prog,
    59  		Attach:  ebpf.AttachCGroupInetEgress,
    60  	})
    61  	testutils.SkipIfNotSupported(t, err)
    62  	if err != nil {
    63  		t.Fatal("Can't create raw link:", err)
    64  	}
    65  
    66  	path := filepath.Join(testutils.TempBPFFS(t), "link")
    67  	err = link.Pin(path)
    68  	testutils.SkipIfNotSupported(t, err)
    69  	if err != nil {
    70  		t.Fatal(err)
    71  	}
    72  
    73  	// It seems like the kernel ignores BPF_F_RDONLY when updating a link,
    74  	// so we can't test this.
    75  	_, err = loadPinnedRawLink(path, &ebpf.LoadPinOptions{
    76  		Flags: math.MaxUint32,
    77  	})
    78  	if !errors.Is(err, unix.EINVAL) {
    79  		t.Fatal("Invalid flags don't trigger an error:", err)
    80  	}
    81  }
    82  
    83  func mustCgroupFixtures(t *testing.T) (*os.File, *ebpf.Program) {
    84  	t.Helper()
    85  
    86  	testutils.SkipIfNotSupported(t, haveProgAttach())
    87  
    88  	return testutils.CreateCgroup(t), mustLoadProgram(t, ebpf.CGroupSKB, 0, "")
    89  }
    90  
    91  func testLink(t *testing.T, link Link, prog *ebpf.Program) {
    92  	t.Helper()
    93  
    94  	tmp, err := os.MkdirTemp("/sys/fs/bpf", "ebpf-test")
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	defer os.RemoveAll(tmp)
    99  
   100  	t.Run("link/pinning", func(t *testing.T) {
   101  		path := filepath.Join(tmp, "link")
   102  		err = link.Pin(path)
   103  		testutils.SkipIfNotSupported(t, err)
   104  		if err != nil {
   105  			t.Fatalf("Can't pin %T: %s", link, err)
   106  		}
   107  
   108  		link2, err := LoadPinnedLink(path, nil)
   109  		if err != nil {
   110  			t.Fatalf("Can't load pinned %T: %s", link, err)
   111  		}
   112  		link2.Close()
   113  
   114  		if reflect.TypeOf(link) != reflect.TypeOf(link2) {
   115  			t.Errorf("Loading a pinned %T returns a %T", link, link2)
   116  		}
   117  
   118  		_, err = LoadPinnedLink(path, &ebpf.LoadPinOptions{
   119  			Flags: math.MaxUint32,
   120  		})
   121  		if !errors.Is(err, unix.EINVAL) {
   122  			t.Errorf("Loading a pinned %T doesn't respect flags", link)
   123  		}
   124  	})
   125  
   126  	t.Run("link/update", func(t *testing.T) {
   127  		err := link.Update(prog)
   128  		testutils.SkipIfNotSupported(t, err)
   129  		if err != nil {
   130  			t.Fatal("Update returns an error:", err)
   131  		}
   132  
   133  		func() {
   134  			// Panicking is OK
   135  			defer func() {
   136  				_ = recover()
   137  			}()
   138  
   139  			if err := link.Update(nil); err == nil {
   140  				t.Fatalf("%T.Update accepts nil program", link)
   141  			}
   142  		}()
   143  	})
   144  
   145  	t.Run("link/info", func(t *testing.T) {
   146  		info, err := link.Info()
   147  		testutils.SkipIfNotSupported(t, err)
   148  		if err != nil {
   149  			t.Fatal("Link info returns an error:", err)
   150  		}
   151  
   152  		if info.Type == 0 {
   153  			t.Fatal("Failed to get link info type")
   154  		}
   155  
   156  		switch info.Type {
   157  		case sys.BPF_LINK_TYPE_TRACING:
   158  			if info.Tracing() == nil {
   159  				t.Fatalf("Failed to get link tracing extra info")
   160  			}
   161  		case sys.BPF_LINK_TYPE_CGROUP:
   162  			cg := info.Cgroup()
   163  			if cg.CgroupId == 0 {
   164  				t.Fatalf("Failed to get link Cgroup extra info")
   165  			}
   166  		case sys.BPF_LINK_TYPE_NETNS:
   167  			netns := info.NetNs()
   168  			if netns.AttachType == 0 {
   169  				t.Fatalf("Failed to get link NetNs extra info")
   170  			}
   171  		case sys.BPF_LINK_TYPE_XDP:
   172  			xdp := info.XDP()
   173  			if xdp.Ifindex == 0 {
   174  				t.Fatalf("Failed to get link XDP extra info")
   175  			}
   176  		}
   177  	})
   178  
   179  	if err := link.Close(); err != nil {
   180  		t.Fatalf("%T.Close returns an error: %s", link, err)
   181  	}
   182  }
   183  
   184  func mustLoadProgram(tb testing.TB, typ ebpf.ProgramType, attachType ebpf.AttachType, attachTo string) *ebpf.Program {
   185  	tb.Helper()
   186  
   187  	license := "MIT"
   188  	switch typ {
   189  	case ebpf.RawTracepoint, ebpf.LSM:
   190  		license = "GPL"
   191  	}
   192  
   193  	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
   194  		Type:       typ,
   195  		AttachType: attachType,
   196  		AttachTo:   attachTo,
   197  		License:    license,
   198  		Instructions: asm.Instructions{
   199  			asm.Mov.Imm(asm.R0, 0),
   200  			asm.Return(),
   201  		},
   202  	})
   203  	if err != nil {
   204  		tb.Fatal(err)
   205  	}
   206  
   207  	tb.Cleanup(func() {
   208  		prog.Close()
   209  	})
   210  
   211  	return prog
   212  }
   213  

View as plain text