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
74
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
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