1 // SPDX-License-Identifier: GPL-2.0
3 #include <test_progs.h>
4 #include "cgroup_helpers.h"
5 #include "testing_helpers.h"
6 #include "test_cgroup_link.skel.h"
8 static __u32 duration
= 0;
9 #define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null"
11 static struct test_cgroup_link
*skel
= NULL
;
13 int ping_and_check(int exp_calls
, int exp_alt_calls
)
16 skel
->bss
->alt_calls
= 0;
17 CHECK_FAIL(system(PING_CMD
));
18 if (CHECK(skel
->bss
->calls
!= exp_calls
, "call_cnt",
19 "exp %d, got %d\n", exp_calls
, skel
->bss
->calls
))
21 if (CHECK(skel
->bss
->alt_calls
!= exp_alt_calls
, "alt_call_cnt",
22 "exp %d, got %d\n", exp_alt_calls
, skel
->bss
->alt_calls
))
27 void test_cgroup_link(void)
36 { "/cg1/cg2/cg3/cg4" },
38 int last_cg
= ARRAY_SIZE(cgs
) - 1, cg_nr
= ARRAY_SIZE(cgs
);
39 DECLARE_LIBBPF_OPTS(bpf_link_update_opts
, link_upd_opts
);
40 struct bpf_link
*links
[ARRAY_SIZE(cgs
)] = {}, *tmp_link
;
41 __u32 prog_ids
[ARRAY_SIZE(cgs
)], prog_cnt
= 0, attach_flags
, prog_id
;
42 struct bpf_link_info info
;
43 int i
= 0, err
, prog_fd
;
44 bool detach_legacy
= false;
46 skel
= test_cgroup_link__open_and_load();
47 if (CHECK(!skel
, "skel_open_load", "failed to open/load skeleton\n"))
49 prog_fd
= bpf_program__fd(skel
->progs
.egress
);
51 err
= setup_cgroup_environment();
52 if (CHECK(err
, "cg_init", "failed: %d\n", err
))
55 for (i
= 0; i
< cg_nr
; i
++) {
56 cgs
[i
].fd
= create_and_get_cgroup(cgs
[i
].path
);
57 if (CHECK(cgs
[i
].fd
< 0, "cg_create", "fail: %d\n", cgs
[i
].fd
))
61 err
= join_cgroup(cgs
[last_cg
].path
);
62 if (CHECK(err
, "cg_join", "fail: %d\n", err
))
65 for (i
= 0; i
< cg_nr
; i
++) {
66 links
[i
] = bpf_program__attach_cgroup(skel
->progs
.egress
,
68 if (CHECK(IS_ERR(links
[i
]), "cg_attach", "i: %d, err: %ld\n",
69 i
, PTR_ERR(links
[i
])))
73 ping_and_check(cg_nr
, 0);
75 /* query the number of effective progs and attach flags in root cg */
76 err
= bpf_prog_query(cgs
[0].fd
, BPF_CGROUP_INET_EGRESS
,
77 BPF_F_QUERY_EFFECTIVE
, &attach_flags
, NULL
,
80 CHECK_FAIL(attach_flags
!= BPF_F_ALLOW_MULTI
);
81 if (CHECK(prog_cnt
!= 1, "effect_cnt", "exp %d, got %d\n", 1, prog_cnt
))
84 /* query the number of effective progs in last cg */
85 err
= bpf_prog_query(cgs
[last_cg
].fd
, BPF_CGROUP_INET_EGRESS
,
86 BPF_F_QUERY_EFFECTIVE
, NULL
, NULL
,
89 CHECK_FAIL(attach_flags
!= BPF_F_ALLOW_MULTI
);
90 if (CHECK(prog_cnt
!= cg_nr
, "effect_cnt", "exp %d, got %d\n",
94 /* query the effective prog IDs in last cg */
95 err
= bpf_prog_query(cgs
[last_cg
].fd
, BPF_CGROUP_INET_EGRESS
,
96 BPF_F_QUERY_EFFECTIVE
, &attach_flags
,
99 CHECK_FAIL(attach_flags
!= BPF_F_ALLOW_MULTI
);
100 if (CHECK(prog_cnt
!= cg_nr
, "effect_cnt", "exp %d, got %d\n",
103 for (i
= 1; i
< prog_cnt
; i
++) {
104 CHECK(prog_ids
[i
- 1] != prog_ids
[i
], "prog_id_check",
105 "idx %d, prev id %d, cur id %d\n",
106 i
, prog_ids
[i
- 1], prog_ids
[i
]);
109 /* detach bottom program and ping again */
110 bpf_link__destroy(links
[last_cg
]);
111 links
[last_cg
] = NULL
;
113 ping_and_check(cg_nr
- 1, 0);
115 /* mix in with non link-based multi-attachments */
116 err
= bpf_prog_attach(prog_fd
, cgs
[last_cg
].fd
,
117 BPF_CGROUP_INET_EGRESS
, BPF_F_ALLOW_MULTI
);
118 if (CHECK(err
, "cg_attach_legacy", "errno=%d\n", errno
))
120 detach_legacy
= true;
122 links
[last_cg
] = bpf_program__attach_cgroup(skel
->progs
.egress
,
124 if (CHECK(IS_ERR(links
[last_cg
]), "cg_attach", "err: %ld\n",
125 PTR_ERR(links
[last_cg
])))
128 ping_and_check(cg_nr
+ 1, 0);
131 bpf_link__destroy(links
[last_cg
]);
132 links
[last_cg
] = NULL
;
135 err
= bpf_prog_detach2(prog_fd
, cgs
[last_cg
].fd
, BPF_CGROUP_INET_EGRESS
);
136 if (CHECK(err
, "cg_detach_legacy", "errno=%d\n", errno
))
138 detach_legacy
= false;
140 /* attach legacy exclusive prog attachment */
141 err
= bpf_prog_attach(prog_fd
, cgs
[last_cg
].fd
,
142 BPF_CGROUP_INET_EGRESS
, 0);
143 if (CHECK(err
, "cg_attach_exclusive", "errno=%d\n", errno
))
145 detach_legacy
= true;
147 /* attempt to mix in with multi-attach bpf_link */
148 tmp_link
= bpf_program__attach_cgroup(skel
->progs
.egress
,
150 if (CHECK(!IS_ERR(tmp_link
), "cg_attach_fail", "unexpected success!\n")) {
151 bpf_link__destroy(tmp_link
);
155 ping_and_check(cg_nr
, 0);
158 err
= bpf_prog_detach2(prog_fd
, cgs
[last_cg
].fd
, BPF_CGROUP_INET_EGRESS
);
159 if (CHECK(err
, "cg_detach_legacy", "errno=%d\n", errno
))
161 detach_legacy
= false;
163 ping_and_check(cg_nr
- 1, 0);
165 /* attach back link-based one */
166 links
[last_cg
] = bpf_program__attach_cgroup(skel
->progs
.egress
,
168 if (CHECK(IS_ERR(links
[last_cg
]), "cg_attach", "err: %ld\n",
169 PTR_ERR(links
[last_cg
])))
172 ping_and_check(cg_nr
, 0);
174 /* check legacy exclusive prog can't be attached */
175 err
= bpf_prog_attach(prog_fd
, cgs
[last_cg
].fd
,
176 BPF_CGROUP_INET_EGRESS
, 0);
177 if (CHECK(!err
, "cg_attach_exclusive", "unexpected success")) {
178 bpf_prog_detach2(prog_fd
, cgs
[last_cg
].fd
, BPF_CGROUP_INET_EGRESS
);
182 /* replace BPF programs inside their links for all but first link */
183 for (i
= 1; i
< cg_nr
; i
++) {
184 err
= bpf_link__update_program(links
[i
], skel
->progs
.egress_alt
);
185 if (CHECK(err
, "prog_upd", "link #%d\n", i
))
189 ping_and_check(1, cg_nr
- 1);
191 /* Attempt program update with wrong expected BPF program */
192 link_upd_opts
.old_prog_fd
= bpf_program__fd(skel
->progs
.egress_alt
);
193 link_upd_opts
.flags
= BPF_F_REPLACE
;
194 err
= bpf_link_update(bpf_link__fd(links
[0]),
195 bpf_program__fd(skel
->progs
.egress_alt
),
197 if (CHECK(err
== 0 || errno
!= EPERM
, "prog_cmpxchg1",
198 "unexpectedly succeeded, err %d, errno %d\n", err
, -errno
))
201 /* Compare-exchange single link program from egress to egress_alt */
202 link_upd_opts
.old_prog_fd
= bpf_program__fd(skel
->progs
.egress
);
203 link_upd_opts
.flags
= BPF_F_REPLACE
;
204 err
= bpf_link_update(bpf_link__fd(links
[0]),
205 bpf_program__fd(skel
->progs
.egress_alt
),
207 if (CHECK(err
, "prog_cmpxchg2", "errno %d\n", -errno
))
211 ping_and_check(0, cg_nr
);
213 /* close cgroup FDs before detaching links */
214 for (i
= 0; i
< cg_nr
; i
++) {
221 /* BPF programs should still get called */
222 ping_and_check(0, cg_nr
);
224 prog_id
= link_info_prog_id(links
[0], &info
);
225 CHECK(prog_id
== 0, "link_info", "failed\n");
226 CHECK(info
.cgroup
.cgroup_id
== 0, "cgroup_id", "unexpected %llu\n", info
.cgroup
.cgroup_id
);
228 err
= bpf_link__detach(links
[0]);
229 if (CHECK(err
, "link_detach", "failed %d\n", err
))
232 /* cgroup_id should be zero in link_info */
233 prog_id
= link_info_prog_id(links
[0], &info
);
234 CHECK(prog_id
== 0, "link_info", "failed\n");
235 CHECK(info
.cgroup
.cgroup_id
!= 0, "cgroup_id", "unexpected %llu\n", info
.cgroup
.cgroup_id
);
237 /* First BPF program shouldn't be called anymore */
238 ping_and_check(0, cg_nr
- 1);
240 /* leave cgroup and remove them, don't detach programs */
241 cleanup_cgroup_environment();
243 /* BPF programs should have been auto-detached */
244 ping_and_check(0, 0);
248 bpf_prog_detach2(prog_fd
, cgs
[last_cg
].fd
,
249 BPF_CGROUP_INET_EGRESS
);
251 for (i
= 0; i
< cg_nr
; i
++) {
252 if (!IS_ERR(links
[i
]))
253 bpf_link__destroy(links
[i
]);
255 test_cgroup_link__destroy(skel
);
257 for (i
= 0; i
< cg_nr
; i
++) {
261 cleanup_cgroup_environment();