github.com/cilium/ebpf@v0.10.0/link/link_test.go (about)

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