Commit | Line | Data |
---|---|---|
d6f39601 AS |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2019 Facebook */ | |
3 | #include <test_progs.h> | |
488a23b8 | 4 | #include <network_helpers.h> |
f6429476 THJ |
5 | #include <bpf/btf.h> |
6 | ||
7 | typedef int (*test_cb)(struct bpf_object *obj); | |
8 | ||
9 | static int check_data_map(struct bpf_object *obj, int prog_cnt, bool reset) | |
10 | { | |
11 | struct bpf_map *data_map = NULL, *map; | |
12 | __u64 *result = NULL; | |
13 | const int zero = 0; | |
14 | __u32 duration = 0; | |
15 | int ret = -1, i; | |
16 | ||
17 | result = malloc((prog_cnt + 32 /* spare */) * sizeof(__u64)); | |
18 | if (CHECK(!result, "alloc_memory", "failed to alloc memory")) | |
19 | return -ENOMEM; | |
20 | ||
21 | bpf_object__for_each_map(map, obj) | |
22 | if (bpf_map__is_internal(map)) { | |
23 | data_map = map; | |
24 | break; | |
25 | } | |
26 | if (CHECK(!data_map, "find_data_map", "data map not found\n")) | |
27 | goto out; | |
28 | ||
29 | ret = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, result); | |
30 | if (CHECK(ret, "get_result", | |
31 | "failed to get output data: %d\n", ret)) | |
32 | goto out; | |
33 | ||
34 | for (i = 0; i < prog_cnt; i++) { | |
35 | if (CHECK(result[i] != 1, "result", | |
36 | "fexit_bpf2bpf result[%d] failed err %llu\n", | |
37 | i, result[i])) | |
38 | goto out; | |
39 | result[i] = 0; | |
40 | } | |
41 | if (reset) { | |
42 | ret = bpf_map_update_elem(bpf_map__fd(data_map), &zero, result, 0); | |
43 | if (CHECK(ret, "reset_result", "failed to reset result\n")) | |
44 | goto out; | |
45 | } | |
46 | ||
47 | ret = 0; | |
48 | out: | |
49 | free(result); | |
50 | return ret; | |
51 | } | |
d6f39601 | 52 | |
8f9081c9 YS |
53 | static void test_fexit_bpf2bpf_common(const char *obj_file, |
54 | const char *target_obj_file, | |
55 | int prog_cnt, | |
1d8a0af5 | 56 | const char **prog_name, |
f6429476 THJ |
57 | bool run_prog, |
58 | test_cb cb) | |
d6f39601 | 59 | { |
f6429476 | 60 | struct bpf_object *obj = NULL, *tgt_obj; |
04fcb5f9 | 61 | __u32 tgt_prog_id, info_len; |
463c2149 | 62 | struct bpf_prog_info prog_info = {}; |
60aed220 | 63 | struct bpf_program **prog = NULL, *p; |
f6429476 | 64 | struct bpf_link **link = NULL; |
f6429476 | 65 | int err, tgt_fd, i; |
463c2149 | 66 | struct btf *btf; |
04fcb5f9 DK |
67 | LIBBPF_OPTS(bpf_test_run_opts, topts, |
68 | .data_in = &pkt_v6, | |
69 | .data_size_in = sizeof(pkt_v6), | |
70 | .repeat = 1, | |
71 | ); | |
d6f39601 | 72 | |
cbdb1461 | 73 | err = bpf_prog_test_load(target_obj_file, BPF_PROG_TYPE_UNSPEC, |
f6429476 | 74 | &tgt_obj, &tgt_fd); |
463c2149 | 75 | if (!ASSERT_OK(err, "tgt_prog_load")) |
d6f39601 | 76 | return; |
d6f39601 | 77 | |
463c2149 THJ |
78 | info_len = sizeof(prog_info); |
79 | err = bpf_obj_get_info_by_fd(tgt_fd, &prog_info, &info_len); | |
80 | if (!ASSERT_OK(err, "tgt_fd_get_info")) | |
81 | goto close_prog; | |
82 | ||
83 | tgt_prog_id = prog_info.id; | |
84 | btf = bpf_object__btf(tgt_obj); | |
85 | ||
8f9081c9 | 86 | link = calloc(sizeof(struct bpf_link *), prog_cnt); |
463c2149 THJ |
87 | if (!ASSERT_OK_PTR(link, "link_ptr")) |
88 | goto close_prog; | |
89 | ||
8f9081c9 | 90 | prog = calloc(sizeof(struct bpf_program *), prog_cnt); |
463c2149 | 91 | if (!ASSERT_OK_PTR(prog, "prog_ptr")) |
8f9081c9 YS |
92 | goto close_prog; |
93 | ||
60aed220 | 94 | obj = bpf_object__open_file(obj_file, NULL); |
463c2149 | 95 | if (!ASSERT_OK_PTR(obj, "obj_open")) |
d6f39601 AS |
96 | goto close_prog; |
97 | ||
60aed220 AN |
98 | bpf_object__for_each_program(p, obj) { |
99 | err = bpf_program__set_attach_target(p, tgt_fd, NULL); | |
100 | ASSERT_OK(err, "set_attach_target"); | |
101 | } | |
102 | ||
d6f39601 | 103 | err = bpf_object__load(obj); |
463c2149 | 104 | if (!ASSERT_OK(err, "obj_load")) |
d6f39601 AS |
105 | goto close_prog; |
106 | ||
8f9081c9 | 107 | for (i = 0; i < prog_cnt; i++) { |
463c2149 | 108 | struct bpf_link_info link_info; |
a393ea80 KFL |
109 | struct bpf_program *pos; |
110 | const char *pos_sec_name; | |
463c2149 THJ |
111 | char *tgt_name; |
112 | __s32 btf_id; | |
113 | ||
114 | tgt_name = strstr(prog_name[i], "/"); | |
115 | if (!ASSERT_OK_PTR(tgt_name, "tgt_name")) | |
116 | goto close_prog; | |
117 | btf_id = btf__find_by_name_kind(btf, tgt_name + 1, BTF_KIND_FUNC); | |
118 | ||
a393ea80 KFL |
119 | prog[i] = NULL; |
120 | bpf_object__for_each_program(pos, obj) { | |
121 | pos_sec_name = bpf_program__section_name(pos); | |
122 | if (pos_sec_name && !strcmp(pos_sec_name, prog_name[i])) { | |
123 | prog[i] = pos; | |
124 | break; | |
125 | } | |
126 | } | |
463c2149 | 127 | if (!ASSERT_OK_PTR(prog[i], prog_name[i])) |
d6f39601 | 128 | goto close_prog; |
463c2149 | 129 | |
d6f39601 | 130 | link[i] = bpf_program__attach_trace(prog[i]); |
463c2149 | 131 | if (!ASSERT_OK_PTR(link[i], "attach_trace")) |
d6f39601 | 132 | goto close_prog; |
463c2149 THJ |
133 | |
134 | info_len = sizeof(link_info); | |
135 | memset(&link_info, 0, sizeof(link_info)); | |
136 | err = bpf_obj_get_info_by_fd(bpf_link__fd(link[i]), | |
137 | &link_info, &info_len); | |
138 | ASSERT_OK(err, "link_fd_get_info"); | |
139 | ASSERT_EQ(link_info.tracing.attach_type, | |
379d19ec | 140 | bpf_program__expected_attach_type(prog[i]), |
463c2149 THJ |
141 | "link_attach_type"); |
142 | ASSERT_EQ(link_info.tracing.target_obj_id, tgt_prog_id, "link_tgt_obj_id"); | |
143 | ASSERT_EQ(link_info.tracing.target_btf_id, btf_id, "link_tgt_btf_id"); | |
d6f39601 | 144 | } |
1d8a0af5 | 145 | |
f6429476 THJ |
146 | if (cb) { |
147 | err = cb(obj); | |
148 | if (err) | |
149 | goto close_prog; | |
150 | } | |
1d8a0af5 | 151 | |
f6429476 | 152 | if (!run_prog) |
d6f39601 AS |
153 | goto close_prog; |
154 | ||
04fcb5f9 | 155 | err = bpf_prog_test_run_opts(tgt_fd, &topts); |
463c2149 | 156 | ASSERT_OK(err, "prog_run"); |
04fcb5f9 | 157 | ASSERT_EQ(topts.retval, 0, "prog_run_ret"); |
d6f39601 | 158 | |
f6429476 | 159 | if (check_data_map(obj, prog_cnt, false)) |
d6f39601 AS |
160 | goto close_prog; |
161 | ||
d6f39601 | 162 | close_prog: |
8f9081c9 | 163 | for (i = 0; i < prog_cnt; i++) |
bad2e478 AN |
164 | bpf_link__destroy(link[i]); |
165 | bpf_object__close(obj); | |
f6429476 | 166 | bpf_object__close(tgt_obj); |
8f9081c9 YS |
167 | free(link); |
168 | free(prog); | |
8f9081c9 YS |
169 | } |
170 | ||
171 | static void test_target_no_callees(void) | |
172 | { | |
173 | const char *prog_name[] = { | |
174 | "fexit/test_pkt_md_access", | |
175 | }; | |
176 | test_fexit_bpf2bpf_common("./fexit_bpf2bpf_simple.o", | |
177 | "./test_pkt_md_access.o", | |
178 | ARRAY_SIZE(prog_name), | |
f6429476 | 179 | prog_name, true, NULL); |
8f9081c9 YS |
180 | } |
181 | ||
182 | static void test_target_yes_callees(void) | |
183 | { | |
184 | const char *prog_name[] = { | |
185 | "fexit/test_pkt_access", | |
186 | "fexit/test_pkt_access_subprog1", | |
187 | "fexit/test_pkt_access_subprog2", | |
7608e4db | 188 | "fexit/test_pkt_access_subprog3", |
8f9081c9 YS |
189 | }; |
190 | test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o", | |
191 | "./test_pkt_access.o", | |
192 | ARRAY_SIZE(prog_name), | |
f6429476 | 193 | prog_name, true, NULL); |
8f9081c9 YS |
194 | } |
195 | ||
7805fe84 AS |
196 | static void test_func_replace(void) |
197 | { | |
198 | const char *prog_name[] = { | |
199 | "fexit/test_pkt_access", | |
200 | "fexit/test_pkt_access_subprog1", | |
201 | "fexit/test_pkt_access_subprog2", | |
202 | "fexit/test_pkt_access_subprog3", | |
203 | "freplace/get_skb_len", | |
204 | "freplace/get_skb_ifindex", | |
205 | "freplace/get_constant", | |
6dc03dc7 | 206 | "freplace/test_pkt_write_access_subprog", |
7805fe84 AS |
207 | }; |
208 | test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o", | |
209 | "./test_pkt_access.o", | |
210 | ARRAY_SIZE(prog_name), | |
f6429476 | 211 | prog_name, true, NULL); |
1d8a0af5 THJ |
212 | } |
213 | ||
214 | static void test_func_replace_verify(void) | |
215 | { | |
216 | const char *prog_name[] = { | |
217 | "freplace/do_bind", | |
218 | }; | |
219 | test_fexit_bpf2bpf_common("./freplace_connect4.o", | |
220 | "./connect4_prog.o", | |
221 | ARRAY_SIZE(prog_name), | |
f6429476 THJ |
222 | prog_name, false, NULL); |
223 | } | |
224 | ||
225 | static int test_second_attach(struct bpf_object *obj) | |
226 | { | |
a393ea80 KFL |
227 | const char *prog_name = "security_new_get_constant"; |
228 | const char *tgt_name = "get_constant"; | |
f6429476 THJ |
229 | const char *tgt_obj_file = "./test_pkt_access.o"; |
230 | struct bpf_program *prog = NULL; | |
231 | struct bpf_object *tgt_obj; | |
f6429476 THJ |
232 | struct bpf_link *link; |
233 | int err = 0, tgt_fd; | |
04fcb5f9 DK |
234 | LIBBPF_OPTS(bpf_test_run_opts, topts, |
235 | .data_in = &pkt_v6, | |
236 | .data_size_in = sizeof(pkt_v6), | |
237 | .repeat = 1, | |
238 | ); | |
f6429476 | 239 | |
a393ea80 | 240 | prog = bpf_object__find_program_by_name(obj, prog_name); |
04fcb5f9 | 241 | if (!ASSERT_OK_PTR(prog, "find_prog")) |
f6429476 THJ |
242 | return -ENOENT; |
243 | ||
cbdb1461 | 244 | err = bpf_prog_test_load(tgt_obj_file, BPF_PROG_TYPE_UNSPEC, |
f6429476 | 245 | &tgt_obj, &tgt_fd); |
04fcb5f9 | 246 | if (!ASSERT_OK(err, "second_prog_load")) |
f6429476 THJ |
247 | return err; |
248 | ||
249 | link = bpf_program__attach_freplace(prog, tgt_fd, tgt_name); | |
bad2e478 | 250 | if (!ASSERT_OK_PTR(link, "second_link")) |
f6429476 THJ |
251 | goto out; |
252 | ||
04fcb5f9 DK |
253 | err = bpf_prog_test_run_opts(tgt_fd, &topts); |
254 | if (!ASSERT_OK(err, "ipv6 test_run")) | |
255 | goto out; | |
256 | if (!ASSERT_OK(topts.retval, "ipv6 retval")) | |
f6429476 THJ |
257 | goto out; |
258 | ||
259 | err = check_data_map(obj, 1, true); | |
260 | if (err) | |
261 | goto out; | |
262 | ||
263 | out: | |
264 | bpf_link__destroy(link); | |
265 | bpf_object__close(tgt_obj); | |
266 | return err; | |
267 | } | |
268 | ||
269 | static void test_func_replace_multi(void) | |
270 | { | |
271 | const char *prog_name[] = { | |
272 | "freplace/get_constant", | |
273 | }; | |
274 | test_fexit_bpf2bpf_common("./freplace_get_constant.o", | |
275 | "./test_pkt_access.o", | |
276 | ARRAY_SIZE(prog_name), | |
277 | prog_name, true, test_second_attach); | |
7805fe84 AS |
278 | } |
279 | ||
bee4b7e6 THJ |
280 | static void test_fmod_ret_freplace(void) |
281 | { | |
282 | struct bpf_object *freplace_obj = NULL, *pkt_obj, *fmod_obj = NULL; | |
283 | const char *freplace_name = "./freplace_get_constant.o"; | |
284 | const char *fmod_ret_name = "./fmod_ret_freplace.o"; | |
285 | DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts); | |
286 | const char *tgt_name = "./test_pkt_access.o"; | |
287 | struct bpf_link *freplace_link = NULL; | |
288 | struct bpf_program *prog; | |
289 | __u32 duration = 0; | |
60aed220 | 290 | int err, pkt_fd, attach_prog_fd; |
bee4b7e6 | 291 | |
cbdb1461 | 292 | err = bpf_prog_test_load(tgt_name, BPF_PROG_TYPE_UNSPEC, |
bee4b7e6 THJ |
293 | &pkt_obj, &pkt_fd); |
294 | /* the target prog should load fine */ | |
295 | if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n", | |
296 | tgt_name, err, errno)) | |
297 | return; | |
bee4b7e6 | 298 | |
60aed220 | 299 | freplace_obj = bpf_object__open_file(freplace_name, NULL); |
bad2e478 | 300 | if (!ASSERT_OK_PTR(freplace_obj, "freplace_obj_open")) |
bee4b7e6 THJ |
301 | goto out; |
302 | ||
6f2b219b | 303 | prog = bpf_object__next_program(freplace_obj, NULL); |
60aed220 AN |
304 | err = bpf_program__set_attach_target(prog, pkt_fd, NULL); |
305 | ASSERT_OK(err, "freplace__set_attach_target"); | |
306 | ||
bee4b7e6 THJ |
307 | err = bpf_object__load(freplace_obj); |
308 | if (CHECK(err, "freplace_obj_load", "err %d\n", err)) | |
309 | goto out; | |
310 | ||
bee4b7e6 | 311 | freplace_link = bpf_program__attach_trace(prog); |
bad2e478 | 312 | if (!ASSERT_OK_PTR(freplace_link, "freplace_attach_trace")) |
bee4b7e6 THJ |
313 | goto out; |
314 | ||
60aed220 | 315 | fmod_obj = bpf_object__open_file(fmod_ret_name, NULL); |
bad2e478 | 316 | if (!ASSERT_OK_PTR(fmod_obj, "fmod_obj_open")) |
bee4b7e6 THJ |
317 | goto out; |
318 | ||
60aed220 | 319 | attach_prog_fd = bpf_program__fd(prog); |
6f2b219b | 320 | prog = bpf_object__next_program(fmod_obj, NULL); |
60aed220 AN |
321 | err = bpf_program__set_attach_target(prog, attach_prog_fd, NULL); |
322 | ASSERT_OK(err, "fmod_ret_set_attach_target"); | |
323 | ||
bee4b7e6 THJ |
324 | err = bpf_object__load(fmod_obj); |
325 | if (CHECK(!err, "fmod_obj_load", "loading fmod_ret should fail\n")) | |
326 | goto out; | |
327 | ||
328 | out: | |
329 | bpf_link__destroy(freplace_link); | |
330 | bpf_object__close(freplace_obj); | |
331 | bpf_object__close(fmod_obj); | |
332 | bpf_object__close(pkt_obj); | |
333 | } | |
334 | ||
335 | ||
1410620c UP |
336 | static void test_func_sockmap_update(void) |
337 | { | |
338 | const char *prog_name[] = { | |
339 | "freplace/cls_redirect", | |
340 | }; | |
341 | test_fexit_bpf2bpf_common("./freplace_cls_redirect.o", | |
342 | "./test_cls_redirect.o", | |
343 | ARRAY_SIZE(prog_name), | |
f6429476 | 344 | prog_name, false, NULL); |
1410620c UP |
345 | } |
346 | ||
347 | static void test_obj_load_failure_common(const char *obj_file, | |
60aed220 | 348 | const char *target_obj_file) |
50d19736 UP |
349 | { |
350 | /* | |
351 | * standalone test that asserts failure to load freplace prog | |
352 | * because of invalid return code. | |
353 | */ | |
354 | struct bpf_object *obj = NULL, *pkt_obj; | |
60aed220 | 355 | struct bpf_program *prog; |
50d19736 UP |
356 | int err, pkt_fd; |
357 | __u32 duration = 0; | |
50d19736 | 358 | |
cbdb1461 | 359 | err = bpf_prog_test_load(target_obj_file, BPF_PROG_TYPE_UNSPEC, |
50d19736 UP |
360 | &pkt_obj, &pkt_fd); |
361 | /* the target prog should load fine */ | |
362 | if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n", | |
363 | target_obj_file, err, errno)) | |
364 | return; | |
50d19736 | 365 | |
60aed220 | 366 | obj = bpf_object__open_file(obj_file, NULL); |
bad2e478 | 367 | if (!ASSERT_OK_PTR(obj, "obj_open")) |
50d19736 UP |
368 | goto close_prog; |
369 | ||
6f2b219b | 370 | prog = bpf_object__next_program(obj, NULL); |
60aed220 AN |
371 | err = bpf_program__set_attach_target(prog, pkt_fd, NULL); |
372 | ASSERT_OK(err, "set_attach_target"); | |
373 | ||
50d19736 UP |
374 | /* It should fail to load the program */ |
375 | err = bpf_object__load(obj); | |
376 | if (CHECK(!err, "bpf_obj_load should fail", "err %d\n", err)) | |
377 | goto close_prog; | |
378 | ||
379 | close_prog: | |
bad2e478 | 380 | bpf_object__close(obj); |
50d19736 UP |
381 | bpf_object__close(pkt_obj); |
382 | } | |
383 | ||
1410620c UP |
384 | static void test_func_replace_return_code(void) |
385 | { | |
386 | /* test invalid return code in the replaced program */ | |
387 | test_obj_load_failure_common("./freplace_connect_v4_prog.o", | |
388 | "./connect4_prog.o"); | |
389 | } | |
390 | ||
391 | static void test_func_map_prog_compatibility(void) | |
392 | { | |
393 | /* test with spin lock map value in the replaced program */ | |
394 | test_obj_load_failure_common("./freplace_attach_probe.o", | |
395 | "./test_attach_probe.o"); | |
396 | } | |
397 | ||
2cf7b7ff THJ |
398 | static void test_func_replace_global_func(void) |
399 | { | |
400 | const char *prog_name[] = { | |
401 | "freplace/test_pkt_access", | |
402 | }; | |
403 | ||
404 | test_fexit_bpf2bpf_common("./freplace_global_func.o", | |
405 | "./test_pkt_access.o", | |
406 | ARRAY_SIZE(prog_name), | |
407 | prog_name, false, NULL); | |
408 | } | |
409 | ||
d3f7b166 YS |
410 | /* NOTE: affect other tests, must run in serial mode */ |
411 | void serial_test_fexit_bpf2bpf(void) | |
8f9081c9 | 412 | { |
d86687ae AN |
413 | if (test__start_subtest("target_no_callees")) |
414 | test_target_no_callees(); | |
415 | if (test__start_subtest("target_yes_callees")) | |
416 | test_target_yes_callees(); | |
417 | if (test__start_subtest("func_replace")) | |
418 | test_func_replace(); | |
419 | if (test__start_subtest("func_replace_verify")) | |
420 | test_func_replace_verify(); | |
421 | if (test__start_subtest("func_sockmap_update")) | |
422 | test_func_sockmap_update(); | |
423 | if (test__start_subtest("func_replace_return_code")) | |
424 | test_func_replace_return_code(); | |
425 | if (test__start_subtest("func_map_prog_compatibility")) | |
426 | test_func_map_prog_compatibility(); | |
f6429476 THJ |
427 | if (test__start_subtest("func_replace_multi")) |
428 | test_func_replace_multi(); | |
bee4b7e6 THJ |
429 | if (test__start_subtest("fmod_ret_freplace")) |
430 | test_fmod_ret_freplace(); | |
2cf7b7ff THJ |
431 | if (test__start_subtest("func_replace_global_func")) |
432 | test_func_replace_global_func(); | |
d6f39601 | 433 | } |