Skip to content

Commit 7b54f95

Browse files
committed
selftests/bpf: Test BPF_LINK_UPDATE behavior for tracing links
Exercise a series of edge cases and happy path scenarios across a gamut of link types (fentry, fmod_ret, fexit, freplace) to test BPF_LINK_UPDATE behavior for tracing links. Signed-off-by: Jordan Rife <[email protected]>
1 parent 627aa36 commit 7b54f95

File tree

2 files changed

+502
-0
lines changed

2 files changed

+502
-0
lines changed
Lines changed: 389 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <network_helpers.h>
4+
#include <test_progs.h>
5+
#include "prog_update_tracing.skel.h"
6+
#include "test_pkt_access.skel.h"
7+
8+
static bool assert_program_order(struct prog_update_tracing *skel_trace,
9+
struct test_pkt_access *skel_pkt,
10+
const struct prog_update_tracing__bss *expected)
11+
{
12+
LIBBPF_OPTS(bpf_test_run_opts, topts_trace);
13+
LIBBPF_OPTS(bpf_test_run_opts, topts_skb,
14+
.data_in = &pkt_v4,
15+
.data_size_in = sizeof(pkt_v4),
16+
.repeat = 1,
17+
);
18+
int prog_fd;
19+
20+
prog_fd = bpf_program__fd(skel_trace->progs.fmod_ret1);
21+
if (!ASSERT_OK(bpf_prog_test_run_opts(prog_fd, &topts_trace),
22+
"bpf_prog_test_run trace"))
23+
return false;
24+
25+
if (!ASSERT_EQ(skel_trace->bss->fentry1_result,
26+
expected->fentry1_result, "fentry1_result"))
27+
return false;
28+
if (!ASSERT_EQ(skel_trace->bss->fentry2_result,
29+
expected->fentry2_result, "fentry2_result"))
30+
return false;
31+
if (!ASSERT_EQ(skel_trace->bss->fmod_ret1_result,
32+
expected->fmod_ret1_result, "fmod_ret1_result"))
33+
return false;
34+
if (!ASSERT_EQ(skel_trace->bss->fmod_ret2_result,
35+
expected->fmod_ret2_result, "fmod_ret2_result"))
36+
return false;
37+
if (!ASSERT_EQ(skel_trace->bss->fexit1_result,
38+
expected->fexit1_result, "fexit1_result"))
39+
return false;
40+
if (!ASSERT_EQ(skel_trace->bss->fexit2_result,
41+
expected->fexit2_result, "fexit2_result"))
42+
return false;
43+
if (!ASSERT_EQ(skel_trace->bss->sequence, expected->sequence,
44+
"sequence"))
45+
return false;
46+
if (!ASSERT_EQ(skel_trace->bss->sequence_bpf, 0, "sequence_bpf"))
47+
return false;
48+
49+
prog_fd = bpf_program__fd(skel_pkt->progs.test_pkt_access);
50+
if (!ASSERT_OK(bpf_prog_test_run_opts(prog_fd, &topts_skb),
51+
"bpf_prog_test_run skb"))
52+
return false;
53+
54+
if (!ASSERT_EQ(skel_trace->bss->sequence, expected->sequence,
55+
"sequence"))
56+
return false;
57+
if (!ASSERT_EQ(skel_trace->bss->fentry1_bpf_result,
58+
expected->fentry1_bpf_result, "fentry1_bpf_result"))
59+
return false;
60+
if (!ASSERT_EQ(skel_trace->bss->fentry2_bpf_result,
61+
expected->fentry2_bpf_result, "fentry2_bpf_result"))
62+
return false;
63+
if (!ASSERT_EQ(skel_trace->bss->freplace1_bpf_result,
64+
expected->freplace1_bpf_result, "freplace1_bpf_result"))
65+
return false;
66+
if (!ASSERT_EQ(skel_trace->bss->freplace2_bpf_result,
67+
expected->freplace2_bpf_result, "freplace2_bpf_result"))
68+
return false;
69+
if (!ASSERT_EQ(skel_trace->bss->fexit1_bpf_result,
70+
expected->fexit1_bpf_result, "fexit1_bpf_result"))
71+
return false;
72+
if (!ASSERT_EQ(skel_trace->bss->fexit2_bpf_result,
73+
expected->fexit2_bpf_result, "fexit2_bpf_result"))
74+
return false;
75+
if (!ASSERT_EQ(skel_trace->bss->sequence_bpf, expected->sequence_bpf,
76+
"sequence_bpf"))
77+
return false;
78+
79+
memset(skel_trace->bss, 0, sizeof(*skel_trace->bss));
80+
81+
return true;
82+
}
83+
84+
void test_prog_update_tracing(void)
85+
{
86+
const struct prog_update_tracing__bss expected_inverted = {
87+
.fentry1_result = 2,
88+
.fentry2_result = 1,
89+
.fmod_ret1_result = 4,
90+
.fmod_ret2_result = 3,
91+
.fexit1_result = 6,
92+
.fexit2_result = 5,
93+
.sequence = 6,
94+
.fentry1_bpf_result = 2,
95+
.fentry2_bpf_result = 1,
96+
.freplace1_bpf_result = 3,
97+
.freplace2_bpf_result = 0,
98+
.fexit1_bpf_result = 5,
99+
.fexit2_bpf_result = 4,
100+
.sequence_bpf = 5,
101+
};
102+
const struct prog_update_tracing__bss expected_normal = {
103+
.fentry1_result = 1,
104+
.fentry2_result = 2,
105+
.fmod_ret1_result = 3,
106+
.fmod_ret2_result = 4,
107+
.fexit1_result = 5,
108+
.fexit2_result = 6,
109+
.sequence = 6,
110+
.fentry1_bpf_result = 1,
111+
.fentry2_bpf_result = 2,
112+
.freplace1_bpf_result = 3,
113+
.freplace2_bpf_result = 0,
114+
.fexit1_bpf_result = 4,
115+
.fexit2_bpf_result = 5,
116+
.sequence_bpf = 5,
117+
};
118+
LIBBPF_OPTS(bpf_link_update_opts, update_opts);
119+
struct prog_update_tracing *skel_trace1 = NULL;
120+
struct prog_update_tracing *skel_trace2 = NULL;
121+
struct test_pkt_access *skel_pkt1 = NULL;
122+
struct test_pkt_access *skel_pkt2 = NULL;
123+
int link_fd, prog_fd, err;
124+
struct bpf_link *link;
125+
126+
skel_trace1 = prog_update_tracing__open();
127+
if (!ASSERT_OK_PTR(skel_trace1, "skel_trace1"))
128+
goto cleanup;
129+
skel_pkt1 = test_pkt_access__open_and_load();
130+
if (!ASSERT_OK_PTR(skel_pkt1, "skel_pkt1"))
131+
goto cleanup;
132+
prog_fd = bpf_program__fd(skel_pkt1->progs.test_pkt_access);
133+
bpf_program__set_attach_target(skel_trace1->progs.fentry1_bpf, prog_fd,
134+
NULL);
135+
bpf_program__set_attach_target(skel_trace1->progs.fentry2_bpf, prog_fd,
136+
NULL);
137+
bpf_program__set_attach_target(skel_trace1->progs.fexit1_bpf, prog_fd,
138+
NULL);
139+
bpf_program__set_attach_target(skel_trace1->progs.fexit2_bpf, prog_fd,
140+
NULL);
141+
bpf_program__set_attach_target(skel_trace1->progs.freplace1_bpf,
142+
prog_fd, "test_pkt_access_subprog3");
143+
bpf_program__set_attach_target(skel_trace1->progs.freplace2_bpf,
144+
prog_fd, "test_pkt_access_subprog3");
145+
bpf_program__set_attach_target(skel_trace1->progs.freplace3_bpf,
146+
prog_fd, "get_skb_ifindex");
147+
err = prog_update_tracing__load(skel_trace1);
148+
if (!ASSERT_OK(err, "skel_trace1 load"))
149+
goto cleanup;
150+
link = bpf_program__attach_trace(skel_trace1->progs.fentry2);
151+
if (!ASSERT_OK_PTR(link, "attach fentry2"))
152+
goto cleanup;
153+
skel_trace1->links.fentry2 = link;
154+
link = bpf_program__attach_trace(skel_trace1->progs.fentry1);
155+
if (!ASSERT_OK_PTR(link, "attach fentry1"))
156+
goto cleanup;
157+
skel_trace1->links.fentry1 = link;
158+
link = bpf_program__attach_trace(skel_trace1->progs.fmod_ret2);
159+
if (!ASSERT_OK_PTR(link, "attach fmod_ret2"))
160+
goto cleanup;
161+
skel_trace1->links.fmod_ret2 = link;
162+
link = bpf_program__attach_trace(skel_trace1->progs.fmod_ret1);
163+
if (!ASSERT_OK_PTR(link, "attach fmod_ret1"))
164+
goto cleanup;
165+
skel_trace1->links.fmod_ret1 = link;
166+
link = bpf_program__attach_trace(skel_trace1->progs.fexit2);
167+
if (!ASSERT_OK_PTR(link, "attach fexit2"))
168+
goto cleanup;
169+
skel_trace1->links.fexit2 = link;
170+
link = bpf_program__attach_trace(skel_trace1->progs.fexit1);
171+
if (!ASSERT_OK_PTR(link, "attach fexit1"))
172+
goto cleanup;
173+
skel_trace1->links.fexit1 = link;
174+
link = bpf_program__attach_trace(skel_trace1->progs.fentry2_bpf);
175+
if (!ASSERT_OK_PTR(link, "attach fentry2_bpf"))
176+
goto cleanup;
177+
skel_trace1->links.fentry2_bpf = link;
178+
link = bpf_program__attach_trace(skel_trace1->progs.fentry1_bpf);
179+
if (!ASSERT_OK_PTR(link, "attach fentry1_bpf"))
180+
goto cleanup;
181+
skel_trace1->links.fentry1_bpf = link;
182+
link = bpf_program__attach_trace(skel_trace1->progs.fexit2_bpf);
183+
if (!ASSERT_OK_PTR(link, "attach fexit2_bpf"))
184+
goto cleanup;
185+
skel_trace1->links.fexit2_bpf = link;
186+
link = bpf_program__attach_trace(skel_trace1->progs.fexit1_bpf);
187+
if (!ASSERT_OK_PTR(link, "attach fexit1_bpf"))
188+
goto cleanup;
189+
skel_trace1->links.fexit1_bpf = link;
190+
link = bpf_program__attach_trace(skel_trace1->progs.freplace1_bpf);
191+
if (!ASSERT_OK_PTR(link, "attach freplace1_bpf"))
192+
goto cleanup;
193+
skel_trace1->links.freplace1_bpf = link;
194+
if (!assert_program_order(skel_trace1, skel_pkt1, &expected_normal))
195+
goto cleanup;
196+
197+
/* Program update should fail if expected_attach_type is a mismatch. */
198+
err = bpf_link__update_program(skel_trace1->links.fentry1,
199+
skel_trace1->progs.fexit1);
200+
if (!ASSERT_EQ(err, -EINVAL,
201+
"fentry1 update wrong expected_attach_type"))
202+
goto cleanup;
203+
if (!assert_program_order(skel_trace1, skel_pkt1, &expected_normal))
204+
goto cleanup;
205+
206+
/* Program update should fail if type is a mismatch. */
207+
err = bpf_link__update_program(skel_trace1->links.fentry1,
208+
skel_pkt1->progs.test_pkt_access);
209+
if (!ASSERT_EQ(err, -EINVAL, "fentry1 update wrong type"))
210+
goto cleanup;
211+
if (!assert_program_order(skel_trace1, skel_pkt1, &expected_normal))
212+
goto cleanup;
213+
214+
/* Program update should fail if program is incompatible */
215+
err = bpf_link__update_program(skel_trace1->links.freplace1_bpf,
216+
skel_trace1->progs.freplace3_bpf);
217+
if (!ASSERT_EQ(err, -EINVAL, "freplace1_bpf update incompatible"))
218+
goto cleanup;
219+
if (!assert_program_order(skel_trace1, skel_pkt1, &expected_normal))
220+
goto cleanup;
221+
222+
/* Update with BPF_F_REPLACE should fail if old_prog_fd does not match
223+
* current link program.
224+
*/
225+
prog_fd = bpf_program__fd(skel_trace1->progs.fentry2);
226+
link_fd = bpf_link__fd(skel_trace1->links.fentry1);
227+
update_opts.old_prog_fd = bpf_program__fd(skel_trace1->progs.fentry2);
228+
update_opts.flags = BPF_F_REPLACE;
229+
err = bpf_link_update(link_fd, prog_fd, &update_opts);
230+
if (!ASSERT_EQ(err, -EPERM, "BPF_F_REPLACE EPERM"))
231+
goto cleanup;
232+
if (!assert_program_order(skel_trace1, skel_pkt1, &expected_normal))
233+
goto cleanup;
234+
235+
/* Do an in-place swap of program order using link updates.
236+
*
237+
* Update with BPF_F_REPLACE should succeed if old_prog_fd matches
238+
* current link program.
239+
* Update should succeed if the new program's dst_trampoline has been
240+
* cleared by a previous attach operation.
241+
*/
242+
update_opts.old_prog_fd = bpf_program__fd(skel_trace1->progs.fentry1);
243+
err = bpf_link_update(link_fd, prog_fd, &update_opts);
244+
if (!ASSERT_OK(err, "BPF_F_REPLACE"))
245+
goto cleanup;
246+
err = bpf_link__update_program(skel_trace1->links.fentry2,
247+
skel_trace1->progs.fentry1);
248+
if (!ASSERT_OK(err, "fentry2 update"))
249+
goto cleanup;
250+
err = bpf_link__update_program(skel_trace1->links.fmod_ret1,
251+
skel_trace1->progs.fmod_ret2);
252+
if (!ASSERT_OK(err, "fmod_ret1 update"))
253+
goto cleanup;
254+
err = bpf_link__update_program(skel_trace1->links.fmod_ret2,
255+
skel_trace1->progs.fmod_ret1);
256+
if (!ASSERT_OK(err, "fmod_ret2 update"))
257+
goto cleanup;
258+
err = bpf_link__update_program(skel_trace1->links.fexit1,
259+
skel_trace1->progs.fexit2);
260+
if (!ASSERT_OK(err, "fexit1 update"))
261+
goto cleanup;
262+
err = bpf_link__update_program(skel_trace1->links.fexit2,
263+
skel_trace1->progs.fexit1);
264+
if (!ASSERT_OK(err, "fexit2 update"))
265+
goto cleanup;
266+
err = bpf_link__update_program(skel_trace1->links.fentry1_bpf,
267+
skel_trace1->progs.fentry2_bpf);
268+
if (!ASSERT_OK(err, "fentry1_bpf update"))
269+
goto cleanup;
270+
err = bpf_link__update_program(skel_trace1->links.fentry2_bpf,
271+
skel_trace1->progs.fentry1_bpf);
272+
if (!ASSERT_OK(err, "fentry2_bpf update"))
273+
goto cleanup;
274+
err = bpf_link__update_program(skel_trace1->links.fexit1_bpf,
275+
skel_trace1->progs.fexit2_bpf);
276+
if (!ASSERT_OK(err, "fexit1_bpf update"))
277+
goto cleanup;
278+
err = bpf_link__update_program(skel_trace1->links.fexit2_bpf,
279+
skel_trace1->progs.fexit1_bpf);
280+
if (!ASSERT_OK(err, "fexit2_bpf update"))
281+
goto cleanup;
282+
/* Only one freplace program can be attached at a time, so instead of
283+
* swapping the order as with fentry/fmod_ret/fexit just shuffle
284+
* freplace1_bpf and freplace_bpf2.
285+
*/
286+
err = bpf_link__destroy(skel_trace1->links.freplace1_bpf);
287+
if (!ASSERT_OK(err, "freplace1_bpf destroy"))
288+
goto cleanup;
289+
skel_trace1->links.freplace1_bpf = NULL;
290+
link = bpf_program__attach_trace(skel_trace1->progs.freplace2_bpf);
291+
if (!ASSERT_OK_PTR(link, "attach freplace2_bpf"))
292+
goto cleanup;
293+
skel_trace1->links.freplace2_bpf = link;
294+
err = bpf_link__update_program(skel_trace1->links.freplace2_bpf,
295+
skel_trace1->progs.freplace1_bpf);
296+
if (!ASSERT_OK(err, "freplace2_bpf update"))
297+
goto cleanup;
298+
if (!assert_program_order(skel_trace1, skel_pkt1, &expected_inverted))
299+
goto cleanup;
300+
301+
/* Update should work when the new program's dst_trampoline points to
302+
* another trampoline or the same trampoline.
303+
*/
304+
skel_trace2 = prog_update_tracing__open();
305+
if (!ASSERT_OK_PTR(skel_trace2, "skel_trace2"))
306+
goto cleanup;
307+
skel_pkt2 = test_pkt_access__open_and_load();
308+
if (!ASSERT_OK_PTR(skel_pkt2, "skel_pkt2"))
309+
goto cleanup;
310+
prog_fd = bpf_program__fd(skel_pkt2->progs.test_pkt_access);
311+
bpf_program__set_attach_target(skel_trace2->progs.fentry1_bpf, prog_fd,
312+
NULL);
313+
bpf_program__set_attach_target(skel_trace2->progs.fentry2_bpf, prog_fd,
314+
NULL);
315+
bpf_program__set_attach_target(skel_trace2->progs.fexit1_bpf, prog_fd,
316+
NULL);
317+
bpf_program__set_attach_target(skel_trace2->progs.fexit2_bpf, prog_fd,
318+
NULL);
319+
bpf_program__set_attach_target(skel_trace2->progs.freplace1_bpf,
320+
prog_fd, "test_pkt_access_subprog3");
321+
bpf_program__set_attach_target(skel_trace2->progs.freplace2_bpf,
322+
prog_fd, "test_pkt_access_subprog3");
323+
bpf_program__set_attach_target(skel_trace2->progs.freplace3_bpf,
324+
prog_fd, "get_skb_ifindex");
325+
err = prog_update_tracing__load(skel_trace2);
326+
if (!ASSERT_OK(err, "skel_trace2 load"))
327+
goto cleanup;
328+
err = bpf_link__update_program(skel_trace1->links.fentry1,
329+
skel_trace2->progs.fentry1);
330+
if (!ASSERT_OK(err, "fentry1 update"))
331+
goto cleanup;
332+
err = bpf_link__update_program(skel_trace1->links.fentry2,
333+
skel_trace2->progs.fentry2);
334+
if (!ASSERT_OK(err, "fentry2 update"))
335+
goto cleanup;
336+
err = bpf_link__update_program(skel_trace1->links.fmod_ret1,
337+
skel_trace2->progs.fmod_ret1);
338+
if (!ASSERT_OK(err, "fmod_ret1 update"))
339+
goto cleanup;
340+
err = bpf_link__update_program(skel_trace1->links.fmod_ret2,
341+
skel_trace2->progs.fmod_ret2);
342+
if (!ASSERT_OK(err, "fmod_ret2 update"))
343+
goto cleanup;
344+
err = bpf_link__update_program(skel_trace1->links.fexit1,
345+
skel_trace2->progs.fexit1);
346+
if (!ASSERT_OK(err, "fexit1 update"))
347+
goto cleanup;
348+
err = bpf_link__update_program(skel_trace1->links.fexit2,
349+
skel_trace2->progs.fexit2);
350+
if (!ASSERT_OK(err, "fexit2 update"))
351+
goto cleanup;
352+
err = bpf_link__update_program(skel_trace1->links.fentry1_bpf,
353+
skel_trace2->progs.fentry1_bpf);
354+
if (!ASSERT_OK(err, "fentry1_bpf update"))
355+
goto cleanup;
356+
err = bpf_link__update_program(skel_trace1->links.fentry2_bpf,
357+
skel_trace2->progs.fentry2_bpf);
358+
if (!ASSERT_OK(err, "fentry2_bpf update"))
359+
goto cleanup;
360+
err = bpf_link__update_program(skel_trace1->links.freplace2_bpf,
361+
skel_trace2->progs.freplace1_bpf);
362+
if (!ASSERT_OK(err, "freplace2_bpf update"))
363+
goto cleanup;
364+
err = bpf_link__update_program(skel_trace1->links.fexit1_bpf,
365+
skel_trace2->progs.fexit1_bpf);
366+
if (!ASSERT_OK(err, "fexit1_bpf update"))
367+
goto cleanup;
368+
err = bpf_link__update_program(skel_trace1->links.fexit2_bpf,
369+
skel_trace2->progs.fexit2_bpf);
370+
if (!ASSERT_OK(err, "fexit2_bpf update"))
371+
goto cleanup;
372+
if (!assert_program_order(skel_trace2, skel_pkt1, &expected_normal))
373+
goto cleanup;
374+
375+
/* Update should work when the new program is the same as the current
376+
* program.
377+
*/
378+
err = bpf_link__update_program(skel_trace1->links.fentry1,
379+
skel_trace2->progs.fentry1);
380+
if (!ASSERT_OK(err, "fentry1 update"))
381+
goto cleanup;
382+
if (!assert_program_order(skel_trace2, skel_pkt1, &expected_normal))
383+
goto cleanup;
384+
cleanup:
385+
prog_update_tracing__destroy(skel_trace1);
386+
prog_update_tracing__destroy(skel_trace2);
387+
test_pkt_access__destroy(skel_pkt1);
388+
test_pkt_access__destroy(skel_pkt2);
389+
}

0 commit comments

Comments
 (0)